diff --git a/app/apis/common/get-news.ts b/app/apis/common/get-news.ts index ab70020..d58aca1 100644 --- a/app/apis/common/get-news.ts +++ b/app/apis/common/get-news.ts @@ -35,8 +35,8 @@ type TParameters = { tags?: string[] } & THttpServer -export const getNews = async (parameters: TParameters) => { - const { categories, tags, ...restParameters } = parameters +export const getNews = async (parameters?: TParameters) => { + const { categories, tags, ...restParameters } = parameters || {} try { const { data } = await HttpServer(restParameters).get(`/api/news`, { params: { diff --git a/app/app.css b/app/app.css index 573dc5b..8751684 100644 --- a/app/app.css +++ b/app/app.css @@ -23,6 +23,10 @@ body { @apply outline-none; } +.ProseMirror-trailingBreak { + @apply hidden; +} + table.dataTable thead > tr { border-bottom: 2px solid #c2c2c2; } diff --git a/app/components/text-editor/index.tsx b/app/components/text-editor/index.tsx index b1c014e..98669a4 100644 --- a/app/components/text-editor/index.tsx +++ b/app/components/text-editor/index.tsx @@ -67,7 +67,13 @@ export const TextEditor = >( const editor = useEditor({ editable: !disabled, extensions: [ - StarterKit, + StarterKit.configure({ + paragraph: { + HTMLAttributes: { + class: 'min-h-1', + }, + }, + }), Highlight, Image.configure({ inline: true, @@ -134,7 +140,7 @@ export const TextEditor = >( editor={editor} id={id ?? generatedId} className={twMerge( - 'prose prose-headings:my-0 prose-p:my-0 max-h-96 max-w-none cursor-text overflow-y-auto rounded-b-md p-2', + 'prose prose-headings:my-0.5 prose-p:my-0.5 max-h-96 max-w-none cursor-text overflow-y-auto rounded-b-md p-2', inputClassName, )} onClick={() => editor?.commands.focus()} diff --git a/app/components/ui/category-section.tsx b/app/components/ui/category-section.tsx index 4046155..e7f9f5a 100644 --- a/app/components/ui/category-section.tsx +++ b/app/components/ui/category-section.tsx @@ -1,14 +1,21 @@ import { useRouteLoaderData } from 'react-router' +import { stripHtml } from 'string-strip-html' import { twMerge } from 'tailwind-merge' +import type { TNewsResponse } from '~/apis/common/get-news' import { CarouselNextIcon } from '~/components/icons/carousel-next' import { CarouselPreviousIcon } from '~/components/icons/carousel-previous' import { Button } from '~/components/ui/button' import { useNewsContext } from '~/contexts/news' import type { loader } from '~/routes/_news' -import type { TNews } from '~/types/news' import { getPremiumAttribute } from '~/utils/render' +type TNews = { + title: string + description: string + items: TNewsResponse[] +} + export const CategorySection = (properties: TNews) => { const { setIsSuccessOpen } = useNewsContext() const loaderData = useRouteLoaderData('routes/_news') @@ -39,7 +46,10 @@ export const CategorySection = (properties: TNews) => {
{items.map( - ({ featured, title, content, tags, slug, isPremium }, index) => ( + ( + { featured_image, title, content, tags, slug, is_premium }, + index, + ) => (
{ className={twMerge( 'aspect-[174/100] w-full rounded-md object-cover sm:aspect-[5/4]', )} - src={featured} + src={featured_image} alt={title} />
@@ -62,10 +72,10 @@ export const CategorySection = (properties: TNews) => { key={index} className="inline-block rounded bg-[#F4F4F4] px-3 py-1 font-bold text-[#777777]" > - {item} + {item.name} ))} - {isPremium && ( + {is_premium && ( Premium Content @@ -80,14 +90,14 @@ export const CategorySection = (properties: TNews) => { > {title} -

- {content} +

+ {stripHtml(content).result}

-
+
{content && htmlParse(content)}
diff --git a/app/routes/_admin.lg-admin._dashboard.contents._index.tsx b/app/routes/_admin.lg-admin._dashboard.contents._index.tsx index b7983d5..4e409e3 100644 --- a/app/routes/_admin.lg-admin._dashboard.contents._index.tsx +++ b/app/routes/_admin.lg-admin._dashboard.contents._index.tsx @@ -1,14 +1,10 @@ import { getNews } from '~/apis/common/get-news' -import { handleCookie } from '~/libs/cookies' import { ContentsPage } from '~/pages/dashboard-contents' import type { Route } from './+types/_admin.lg-admin._dashboard.contents._index' -export const loader = async ({ request }: Route.LoaderArgs) => { - const { staffToken } = await handleCookie(request) - const { data: newsData } = await getNews({ - accessToken: staffToken, - }) +export const loader = async ({}: Route.LoaderArgs) => { + const { data: newsData } = await getNews() return { newsData } } diff --git a/app/routes/_news.category.$code.tsx b/app/routes/_news.category.$code.tsx index 1071fb2..3aab2d1 100644 --- a/app/routes/_news.category.$code.tsx +++ b/app/routes/_news.category.$code.tsx @@ -1,4 +1,6 @@ import { getCategories } from '~/apis/common/get-categories' +import { getNews } from '~/apis/common/get-news' +import { APP } from '~/configs/meta' import { NewsCategoriesPage } from '~/pages/news-categories' import type { Route } from './+types/_news.category.$code' @@ -8,7 +10,20 @@ export const loader = async ({ params }: Route.LoaderArgs) => { const categoryData = categoriesData.find( (category) => category.code === params.code, ) - return { categoryData } + const { data: newsData } = await getNews({ categories: [params.code] }) + return { categoryData, newsData } +} + +export const meta = ({ data }: Route.MetaArgs) => { + const { categoryData } = data + const metaTitle = APP.title + const title = `${categoryData?.name} - ${metaTitle}` + + return [ + { + title, + }, + ] } const NewsCategoriesLayout = () => diff --git a/package.json b/package.json index 871dc8e..5f93d18 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-router": "^7.1.3", "react-share": "^5.2.2", "remix-hook-form": "^6.1.3", + "string-strip-html": "^13.4.12", "tailwind-merge": "^3.0.1", "xior": "^0.6.3", "zod": "^3.24.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f950c1..9c10c9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,6 +104,9 @@ importers: remix-hook-form: specifier: ^6.1.3 version: 6.1.3(react-dom@19.0.0(react@19.0.0))(react-hook-form@7.54.2(react@19.0.0))(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + string-strip-html: + specifier: ^13.4.12 + version: 13.4.12 tailwind-merge: specifier: ^3.0.1 version: 3.0.1 @@ -1653,6 +1656,9 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + '@types/lodash@4.17.16': resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} @@ -1993,6 +1999,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + codsen-utils@1.6.7: + resolution: {integrity: sha512-M+9D3IhFAk4T8iATX62herVuIx1sp5kskWgxEegKD/JwTTSSGjGQs5Q5J4vVJ4mLcn1uhfxDYv6Yzr8zleHF3w==} + engines: {node: '>=14.18.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2827,6 +2837,9 @@ packages: html-dom-parser@5.0.13: resolution: {integrity: sha512-B7JonBuAfG32I7fDouUQEogBrz3jK9gAuN1r1AaXpED6dIhtg/JwiSRhjGL7aOJwRz3HU4efowCjQBaoXiREqg==} + html-entities@2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + html-react-parser@5.2.2: resolution: {integrity: sha512-yA5012CJGSFWYZsgYzfr6HXJgDap38/AEP4ra8Cw+WHIi2ZRDXRX/QVYdumRf1P8zKyScKd6YOrWYvVEiPfGKg==} peerDependencies: @@ -3272,6 +3285,9 @@ packages: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -3857,6 +3873,22 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} + ranges-apply@7.0.19: + resolution: {integrity: sha512-imA03KuTSuSpQtq9SDhavUz7BtiddCPj+fsYM/XpdypRN/s8vyTayKzni6m5nYs7VMds1kSNK1V3jfwVrPUWBQ==} + engines: {node: '>=14.18.0'} + + ranges-merge@9.0.18: + resolution: {integrity: sha512-2+6Eh4yxi5sudUmvCdvxVOSdXIXV+Brfutw8chhZmqkT0REqlzilpyQps1S5n8c7f0+idblqSAHGahTbf/Ar5g==} + engines: {node: '>=14.18.0'} + + ranges-push@7.0.18: + resolution: {integrity: sha512-wzGHipEklSlY0QloQ88PNt+PkTURIB42PLLcQGY+WyYBlNpnrzps6EYooD3RqNXtdqMQ9kR8IVaF9itRYtuzLA==} + engines: {node: '>=14.18.0'} + + ranges-sort@6.0.13: + resolution: {integrity: sha512-M3P0/dUnU3ihLPX2jq0MT2NJA1ls/q6cUAUVPD28xdFFqm3VFarPjTKKhnsBSvYCpZD8HdiElAGAyoPu6uOQjA==} + engines: {node: '>=14.18.0'} + raw-body@2.5.2: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} @@ -4246,6 +4278,22 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-collapse-leading-whitespace@7.0.9: + resolution: {integrity: sha512-lEuTHlogBT9PWipfk0FOyvoMKX8syiE03QoFk5MDh8oS0AJ2C07IlstR5cGkxz48nKkOIuvkC28w9Rx/cVRNDg==} + engines: {node: '>=14.18.0'} + + string-left-right@6.0.20: + resolution: {integrity: sha512-dz2mUgmsI7m/FMe+BoxZ2+73X1TUoQvjCdnq8vbIAnHlvWfVZleNUR+lw+QgHA2dlJig+hUWC9bFYdNFGGy2bA==} + engines: {node: '>=14.18.0'} + + string-strip-html@13.4.12: + resolution: {integrity: sha512-mr1GM1TFcwDkYwLE7TNkHY+Lf3YFEBa19W9KntZoJJSbrKF07W4xmLkPnqf8cypEGyr+dc1H9hsdTw5VSNVGxg==} + engines: {node: '>=14.18.0'} + + string-trim-spaces-only@5.0.12: + resolution: {integrity: sha512-Un5nIO1av+hzfnKGmY+bWe0AD4WH37TuDW+jeMPm81rUvU2r3VPRj9vEKdZkPmuhYAMuKlzarm7jDSKwJKOcpQ==} + engines: {node: '>=14.18.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4358,6 +4406,9 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-lru@11.2.11: resolution: {integrity: sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA==} engines: {node: '>=12'} @@ -6216,6 +6267,10 @@ snapshots: '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.16 + '@types/lodash@4.17.16': {} '@types/markdown-it@14.1.2': @@ -6615,6 +6670,10 @@ snapshots: clsx@2.1.1: {} + codsen-utils@1.6.7: + dependencies: + rfdc: 1.4.1 + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -7632,6 +7691,8 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.0.0 + html-entities@2.5.2: {} + html-react-parser@5.2.2(@types/react@19.0.8)(react@19.0.0): dependencies: domhandler: 5.0.3 @@ -8061,6 +8122,8 @@ snapshots: dependencies: p-locate: 6.0.0 + lodash-es@4.17.21: {} + lodash.camelcase@4.3.0: {} lodash.castarray@4.4.0: {} @@ -8586,6 +8649,25 @@ snapshots: range-parser@1.2.1: {} + ranges-apply@7.0.19: + dependencies: + ranges-merge: 9.0.18 + tiny-invariant: 1.3.3 + + ranges-merge@9.0.18: + dependencies: + ranges-push: 7.0.18 + ranges-sort: 6.0.13 + + ranges-push@7.0.18: + dependencies: + codsen-utils: 1.6.7 + ranges-sort: 6.0.13 + string-collapse-leading-whitespace: 7.0.9 + string-trim-spaces-only: 5.0.12 + + ranges-sort@6.0.13: {} + raw-body@2.5.2: dependencies: bytes: 3.1.2 @@ -9045,6 +9127,25 @@ snapshots: string-argv@0.3.2: {} + string-collapse-leading-whitespace@7.0.9: {} + + string-left-right@6.0.20: + dependencies: + codsen-utils: 1.6.7 + rfdc: 1.4.1 + + string-strip-html@13.4.12: + dependencies: + '@types/lodash-es': 4.17.12 + codsen-utils: 1.6.7 + html-entities: 2.5.2 + lodash-es: 4.17.21 + ranges-apply: 7.0.19 + ranges-push: 7.0.18 + string-left-right: 6.0.20 + + string-trim-spaces-only@5.0.12: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -9174,6 +9275,8 @@ snapshots: through@2.3.8: {} + tiny-invariant@1.3.3: {} + tiny-lru@11.2.11: {} tinyexec@0.3.2: {}