diff --git a/frontend/package.json b/frontend/package.json index 976e15290..6d7f627c1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -52,6 +52,7 @@ "@types/styled-components": "5.1.26", "@typescript-eslint/eslint-plugin": "5.61.0", "@typescript-eslint/parser": "5.61.0", + "copy-webpack-plugin": "^11.0.0", "cross-env": "^7.0.3", "css-loader": "^6.8.1", "cypress": "^12.17.3", diff --git a/frontend/src/assets/icons/empty-writing-table.svg b/frontend/src/assets/icons/empty-writing-table.svg new file mode 100644 index 000000000..549573a88 --- /dev/null +++ b/frontend/src/assets/icons/empty-writing-table.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/icons/index.ts b/frontend/src/assets/icons/index.ts index c35a6879c..65900c3e0 100644 --- a/frontend/src/assets/icons/index.ts +++ b/frontend/src/assets/icons/index.ts @@ -30,4 +30,5 @@ export { ReactComponent as HomeBorderIcon } from './home-border.svg'; export { ReactComponent as TimeIcon } from './time.svg'; export { ReactComponent as PasswordIcon } from './password.svg'; export { ReactComponent as PublishIcon } from './publish.svg'; +export { ReactComponent as EmptyWritingTableIcon } from './empty-writing-table.svg'; export { ReactComponent as CategoryIcon } from './category.svg'; diff --git a/frontend/src/assets/icons/trash-can-empty.svg b/frontend/src/assets/icons/trash-can-empty.svg index 45d3b2602..ea4499d1b 100644 --- a/frontend/src/assets/icons/trash-can-empty.svg +++ b/frontend/src/assets/icons/trash-can-empty.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/HomeTable/HomeTable.tsx b/frontend/src/components/HomeTable/HomeTable.tsx index 898ba6e04..5f72a2013 100644 --- a/frontend/src/components/HomeTable/HomeTable.tsx +++ b/frontend/src/components/HomeTable/HomeTable.tsx @@ -5,6 +5,7 @@ import { dateFormatter } from 'utils/date'; import { blogIcon } from 'components/WritingTable/WritingTable'; import Pagination from 'components/@common/Pagination/Pagination'; import { useHomeTable } from './useHomeTable'; +import { EmptyWritingTableIcon } from 'assets/icons'; type Props = { initialPageIndex?: number; @@ -15,7 +16,16 @@ const HomeTable = ({ initialPageIndex = 0 }: Props) => { useHomeTable(initialPageIndex); const { goWritingPage } = usePageNavigate(); - if (!content || !totalPages) return 글을 추가해 주세요😊; + if (!content || !totalPages) + return ( + + + + 글이 없습니다. + 글 가져오기를 통해 글을 추가해 보세요! + + + ); return ( @@ -80,16 +90,34 @@ export default HomeTable; const S = { Container: styled.div` display: flex; + position: relative; flex-direction: column; justify-content: center; align-items: center; gap: 50px; `, - AddWritingText: styled.p` - font-size: 1.5rem; + EmptyMessage: styled.p` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 1.6rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; `, + AddWritingTextContainer: styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + `, + + AddWritingText: styled.p``, + HomeTable: styled.table` width: 100%; text-align: left; diff --git a/frontend/src/components/HomeTable/useHomeTable.ts b/frontend/src/components/HomeTable/useHomeTable.ts index 967a2d4da..11186ec2c 100644 --- a/frontend/src/components/HomeTable/useHomeTable.ts +++ b/frontend/src/components/HomeTable/useHomeTable.ts @@ -7,8 +7,10 @@ import { GetHomeWritingsResponse } from 'types/apis/writings'; export const useHomeTable = (initialPageIndex: number) => { const [activePage, setActivePage] = useState(initialPageIndex); const [fetchOption, setFetchOption] = useState(`?page=${activePage}`); - const { data } = useQuery(['homeWritings', fetchOption], () => - getHomeWritings(fetchOption), + const { data } = useQuery( + ['homeWritings', fetchOption], + () => getHomeWritings(fetchOption), + { suspense: true }, ); const rowRef = useRef(null); diff --git a/frontend/src/hooks/@common/useCodeHighlight.ts b/frontend/src/hooks/@common/useCodeHighlight.ts index ffc8b75f0..48ddf0550 100644 --- a/frontend/src/hooks/@common/useCodeHighlight.ts +++ b/frontend/src/hooks/@common/useCodeHighlight.ts @@ -1,27 +1,20 @@ import { useEffect } from 'react'; import Prism from 'prismjs'; import 'prismjs/themes/prism.css'; +import 'prismjs/plugins/autoloader/prism-autoloader.js'; -const prismLanguageFromCodeTagRegex = //g; +// webpack copyWebpackPlugin 이용해 prismjs/components/경로에 언어모듈 저장 +Prism.plugins.autoloader.languages_path = '/prismjs/components/'; const useCodeHighlight = (htmlDOMString?: string) => { useEffect(() => { if (!htmlDOMString) return; - const importPrism = async () => { - const languages = Array.from(htmlDOMString.matchAll(prismLanguageFromCodeTagRegex)).map( - (match) => match[1], - ); - - await Promise.all( - languages.map((language) => import(`prismjs/components/prism-${language}`)), - ); - }; const highlightCode = () => { Prism.highlightAll(); }; - importPrism().then(highlightCode); + highlightCode(); }, [htmlDOMString]); }; diff --git a/frontend/src/mocks/data/writingPage.ts b/frontend/src/mocks/data/writingPage.ts index c6905bd19..70fcc6cf5 100644 --- a/frontend/src/mocks/data/writingPage.ts +++ b/frontend/src/mocks/data/writingPage.ts @@ -1,7 +1,146 @@ import { GetWritingPropertiesResponse } from 'types/apis/writings'; const writingContentMock = ` -

동글이란?

블로그 글 관리의 새로운 해결책

노션 같은 텍스트 에디터 에서 작성한 글을 블로그에 올린 적이 있으신가요?

직접 글을 블로그로 올리다보면 그 과정이 귀찮아지고 어떤 글을 옮겼는지 헷갈릴 때가 많습니다.

이 문제를 해결하기 위해 우리는 동글 서비스를 만들었습니다.

주요 기능

  1. 노션 글 업로드: 노션에 작성한 글을 간편하게 동글에 업로드할 수 있습니다. (마크다운 파일 업로드도 지원)
  2. 카테고리 분류: 동글은 업로드한 글을 카테고리로 분류하여 모아볼 수 있는 기능을 제공합니다.
  3. 다양한 블로그 플랫폼 지원: 작성한 글을 Tistory나 Medium와 같은 블로그 플랫폼에 발행할 수 있습니다.
  4. 발행 정보 투명화: 글의 작성 일자와 발행된 블로그 정보를 통해 글을 효율적으로 관리할 수 있습니다.

다양한 곳에서 글을 작성 후 쉽게 블로그로 포스팅하고 싶은 분들은 동글의 도움을 받아보세요.

더 많은 시간과 에너지를 여러분의 글 작성과 이야기에 투자할 수 있을 것입니다.

동글과 함께라면 블로그 글 관리는 더 이상 고민거리가 아닙니다.

이제 글 작성에 더 집중하며, 블로그 관리에 소비되는 시간과 에너지를 절약하세요!

`; +

동글이란?

블로그 글 관리의 새로운 해결책

노션 같은 텍스트 에디터 에서 작성한 글을 블로그에 올린 적이 있으신가요?

직접 글을 블로그로 올리다보면 그 과정이 귀찮아지고 어떤 글을 옮겼는지 헷갈릴 때가 많습니다.

이 문제를 해결하기 위해 우리는 동글 서비스를 만들었습니다.

주요 기능

  1. 노션 글 업로드: 노션에 작성한 글을 간편하게 동글에 업로드할 수 있습니다. (마크다운 파일 업로드도 지원)
  2. 카테고리 분류: 동글은 업로드한 글을 카테고리로 분류하여 모아볼 수 있는 기능을 제공합니다.
  3. 다양한 블로그 플랫폼 지원: 작성한 글을 Tistory나 Medium와 같은 블로그 플랫폼에 발행할 수 있습니다.
  4. 발행 정보 투명화: 글의 작성 일자와 발행된 블로그 정보를 통해 글을 효율적으로 관리할 수 있습니다.

다양한 곳에서 글을 작성 후 쉽게 블로그로 포스팅하고 싶은 분들은 동글의 도움을 받아보세요.

더 많은 시간과 에너지를 여러분의 글 작성과 이야기에 투자할 수 있을 것입니다.

동글과 함께라면 블로그 글 관리는 더 이상 고민거리가 아닙니다.

이제 글 작성에 더 집중하며, 블로그 관리에 소비되는 시간과 에너지를 절약하세요!


+function helloWorld() {
+  console.log("Hello, World!");
+}
+

+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Hello World</title>
+</head>
+<body>
+    <h1>Hello, World!</h1>
+</body>
+</html>
+
+ +
+

+def hello_world():
+    print("Hello, World!")
+
+

+public class HelloWorld {
+    public static void main(String[] args) {
+        System.out.println("Hello, World!");
+    }
+}
+
+

+public class HelloWorld {
+    public static void Main(string[] args) {
+        Console.WriteLine("Hello, World!");
+    }
+}
+
+

+def hello_world
+  puts "Hello, World!"
+end
+
+

+<?php
+function helloWorld() {
+  echo "Hello, World!";
+}
+?>
+
+

+package main
+
+import "fmt"
+
+func main() {
+    fmt.Println("Hello, World!")
+}
+
+

+function helloWorld(): void {
+  console.log("Hello, World!");
+}
+
+

+func helloWorld() {
+    print("Hello, World!")
+}
+
+

+#include <iostream>
+
+int main() {
+    std::cout << "Hello, World!" << std::endl;
+    return 0;
+}
+
+

+fn main() {
+    println!("Hello, World!");
+}
+
+

+fun main() {
+    println("Hello, World!")
+}
+
+

+print "Hello, World!\n";
+
+

+SELECT 'Hello, World!' AS greeting;
+
+

+main :: IO ()
+main = putStrLn "Hello, World!"
+
+

+print("Hello, World!")
+
+

+echo "Hello, World!"
+
+

+disp('Hello, World!');
+
+

+object HelloWorld {
+  def main(args: Array[String]): Unit = {
+    println("Hello, World!")
+  }
+}
+
+

+println 'Hello, World!'
+
+

+print("Hello, World!")
+
+

+PROGRAM HelloWorld
+   PRINT *, 'Hello, World!'
+END PROGRAM HelloWorld
+
+

+program HelloWorld;
+begin
+  writeln('Hello, World!');
+end.
+
+

+IO.puts "Hello, World!"
+
+

+hello_world :- write('Hello, World!'), nl.
+
+

+(print "Hello, World!")
+
+ +`; export const writing = { id: 1, diff --git a/frontend/src/pages/HomePage/HomePage.tsx b/frontend/src/pages/HomePage/HomePage.tsx index fed63bac1..a6df339b1 100644 --- a/frontend/src/pages/HomePage/HomePage.tsx +++ b/frontend/src/pages/HomePage/HomePage.tsx @@ -1,11 +1,22 @@ +import Spinner from 'components/@common/Spinner/Spinner'; import HomeTable from 'components/HomeTable/HomeTable'; +import { Suspense } from 'react'; import styled from 'styled-components'; const HomePage = () => { return ( - 전체 글 - + + +

전체 글을 불러오는 중입니다 ...

+ + } + > + 전체 글 + +
); }; @@ -13,6 +24,16 @@ const HomePage = () => { export default HomePage; const S = { + LoadingContainer: styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 2rem; + max-width: 100%; + height: 100%; + `, + Article: styled.article` position: relative; width: 100%; diff --git a/frontend/src/pages/TrashCanPage/TrashCanPage.tsx b/frontend/src/pages/TrashCanPage/TrashCanPage.tsx index 58c3b75f9..47529ec6b 100644 --- a/frontend/src/pages/TrashCanPage/TrashCanPage.tsx +++ b/frontend/src/pages/TrashCanPage/TrashCanPage.tsx @@ -23,7 +23,7 @@ const TrashCanPage = () => { ) : ( - + 휴지통이 비어있어요. )} @@ -56,7 +56,7 @@ const S = { display: flex; flex-direction: column; align-items: center; - gap: 1.5rem; + gap: 2rem; `, LoadingContainer: styled.div` diff --git a/frontend/src/pages/WritingTablePage/WritingTablePage.tsx b/frontend/src/pages/WritingTablePage/WritingTablePage.tsx index 9156e93b2..0da4fbdb5 100644 --- a/frontend/src/pages/WritingTablePage/WritingTablePage.tsx +++ b/frontend/src/pages/WritingTablePage/WritingTablePage.tsx @@ -1,6 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import { useSetGlobalState } from '@yogjin/react-global-state'; import { getDetailWritings } from 'apis/writings'; +import { EmptyWritingTableIcon } from 'assets/icons'; import Spinner from 'components/@common/Spinner/Spinner'; import WritingTable from 'components/WritingTable/WritingTable'; import { activeCategoryIdState } from 'globalState'; @@ -38,7 +39,13 @@ const WritingTablePage = () => { {data?.writings && data.writings.length > 0 ? ( ) : ( - 카테고리에 글을 추가해주세요😊 + + + + 카테고리가 비어있어요. + 글 가져오기를 통해 글을 추가해 보세요! + + )} ); @@ -48,6 +55,7 @@ export default WritingTablePage; const S = { Article: styled.article` + position: relative; width: 100%; padding: 8rem; `, @@ -57,9 +65,7 @@ const S = { margin-bottom: 5rem; `, - AddWritingText: styled.p` - font-size: 1.5rem; - `, + AddWritingText: styled.p``, SidebarSection: styled.section` ${sidebarStyle} @@ -74,4 +80,23 @@ const S = { max-width: 100%; height: 100%; `, + + EmptyMessage: styled.p` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 1.6rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + `, + + AddWritingTextContainer: styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + `, }; diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 58256f1de..064d3197f 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -2,6 +2,7 @@ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const CopyPlugin = require('copy-webpack-plugin'); module.exports = { mode: process.env.NODE_ENV === 'development' ? 'development' : 'production', @@ -70,6 +71,9 @@ module.exports = { generateStatsFile: true, statsFilename: 'bundle-report.json', }), + new CopyPlugin({ + patterns: [{ from: 'node_modules/prismjs/components/', to: 'prismjs/components/' }], + }), ], devServer: { hot: true, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7dbb92143..5ee48989c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5242,6 +5242,18 @@ copy-anything@^3.0.2: dependencies: is-what "^4.1.8" +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + core-js-compat@^3.25.1: version "3.31.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" @@ -6572,6 +6584,17 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.2.11: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" @@ -7041,7 +7064,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.2: +glob-parent@^6.0.1, glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -7121,7 +7144,7 @@ globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.3: +globby@^13.1.1, globby@^13.1.3: version "13.2.2" resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== @@ -10447,7 +10470,7 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -serialize-javascript@^6.0.1: +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==