@@ -68,7 +99,18 @@ const Home = () => {

Uudet sisällöt

-

TODO

+
+ {newContent.map((c) => ( + + ))} +

Suositut sisällöt

diff --git a/src/routes/Home/index.ts b/src/routes/Home/index.ts index d213ecf..9450de0 100644 --- a/src/routes/Home/index.ts +++ b/src/routes/Home/index.ts @@ -1,3 +1,4 @@ import Home from './Home'; +import loader from './loader'; -export { Home }; +export { Home, loader as homeLoader }; diff --git a/src/routes/Home/loader.ts b/src/routes/Home/loader.ts new file mode 100644 index 0000000..4d3e8e3 --- /dev/null +++ b/src/routes/Home/loader.ts @@ -0,0 +1,19 @@ +import { StructuredContentPage } from '@/types/cms-content'; +import { LoaderFunction } from 'react-router'; + +const loader = (async () => { + const queryParams = new URLSearchParams(); + queryParams.set('sort', 'dateCreated:desc'); + const response = await fetch(`cms/o/headless-delivery/v1.0/sites/20117/structured-contents?${queryParams}`, { + headers: { + Accept: 'application/json', + 'Accept-Language': 'fi-FI', + }, + }); + const data: StructuredContentPage = await response.json(); + + return { data }; +}) satisfies LoaderFunction; + +export type LoaderData = Awaited>; +export default loader; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index ff8a98b..568aec4 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,6 +1,6 @@ import i18n from '@/i18n/config'; import { RouteObject, redirect } from 'react-router'; -import { Home } from './Home'; +import { Home, homeLoader } from './Home'; import { NoMatch, Root } from './Root'; const rootRoute: RouteObject = { @@ -11,6 +11,7 @@ const rootRoute: RouteObject = { { index: true, element: , + loader: homeLoader, }, ], }; diff --git a/src/types/cms-content.ts b/src/types/cms-content.ts new file mode 100644 index 0000000..d64d276 --- /dev/null +++ b/src/types/cms-content.ts @@ -0,0 +1,76 @@ +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/TaxonomyCategoryBrief +export interface TaxonomyBrief { + taxonomyCategoryId: number; + taxonomyCategoryName: string; +} + +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/ContentDocument +export interface ContentDocument { + contentType: string; + contentUrl: string; + description: string; + encodingFormat: string; + fileExtension: string; + id: number; + sizeInBytes: number; + title: string; +} + +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/ContentFieldValue +export interface ContentFieldValue { + data?: string; + image?: ContentDocument; + geo?: Record; + link?: string; +} + +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/ContentField +export interface ContentField { + contentFieldValue: ContentFieldValue; + dataType: string; + inputControl: string; + label: string; + name: string; + nestedContentFields: unknown[]; + repeatable: boolean; +} + +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/StructuredContent +export interface StructuredContent { + actions?: Record; + availableLanguages?: string[]; + contentFields?: ContentField[]; + contentStructureId: number; + creator?: Record; + customFields?: unknown[]; + dateCreated?: string; + dateModified?: string; + description?: string; + externalReferenceCode?: string; + friendlyUrlPath?: string; + id?: number; + key?: number; + keywords?: string[]; + neverExpire?: boolean; + numberOfComments?: number; + priority?: number; + relatedContents?: unknown[]; + renderedContents?: Record[]; + siteId?: number; + strucuturedContentFolderId?: number; + subscribed?: boolean; + taxonomyCategoryBriefs?: TaxonomyBrief[]; + title: string; + uuid?: string; +} + +// https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/StructuredContent/getSiteStructuredContentsPage +export interface StructuredContentPage { + actions: Record; + facets: unknown[]; + items: StructuredContent[]; + lastPage: number; + page: number; + pageSize: number; + totalCount: number; +} diff --git a/src/utils/cms.ts b/src/utils/cms.ts new file mode 100644 index 0000000..eb36475 --- /dev/null +++ b/src/utils/cms.ts @@ -0,0 +1,13 @@ +import { StructuredContent } from '@/types/cms-content'; + +type ContentLabel = 'Tiivistelmä' | 'Kuvaus' | 'Kuva' | 'Tiedosto' | 'Linkki'; + +/** + * Finds the content value from Liferay strucured content by label + * @param item Structured content item + * @param {ContentLabel} label Content label + * @returns {ContentFieldValue | undefined} Content value + */ +export const findContentValueByLabel = (item: StructuredContent, label: ContentLabel) => { + return item.contentFields?.find((field) => field.label === label)?.contentFieldValue; +}; diff --git a/vite.config.ts b/vite.config.ts index c66e0c4..4ba4064 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,30 +1,57 @@ /// import react from '@vitejs/plugin-react-swc'; import { fileURLToPath } from 'node:url'; -import { defineConfig } from 'vite'; +import { defineConfig, loadEnv } from 'vite'; + +const cmsHeaders = (env: Record) => ({ + Cookie: `JODTOKEN=${env.JODTOKEN}`, + Accept: 'application/json', + Authorization: 'Basic ' + Buffer.from(`${env.CMSUSER}:${env.CMSPASSWORD}`).toString('base64'), +}); // https://vite.dev/config/ -export default defineConfig({ - base: '/ohjaaja/', - plugins: [react()], - test: { - environment: 'jsdom', - globals: true, - setupFiles: ['./vitest.setup.ts'], - coverage: { - provider: 'v8', - reporter: ['lcov'], +export default defineConfig(({ mode }) => { + // loadEnv is required to read .env files + const env = loadEnv(mode, process.cwd(), ''); + const target = env.CMSURL; + + return { + base: '/ohjaaja/', + plugins: [react()], + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./vitest.setup.ts'], + coverage: { + provider: 'v8', + reporter: ['lcov'], + }, + }, + resolve: { + alias: [ + { + find: '@', + replacement: fileURLToPath(new URL('./src', import.meta.url)), + }, + ], }, - }, - resolve: { - alias: [ - { - find: '@', - replacement: fileURLToPath(new URL('./src', import.meta.url)), + server: { + port: 8080, + proxy: { + '/ohjaaja/cms': { + target, + changeOrigin: true, + xfwd: true, + rewrite: (path) => path.replace(/^\/ohjaaja\/cms/, '/cms'), + headers: cmsHeaders(env), + }, + '/cms/documents': { + target, + changeOrigin: true, + xfwd: true, + headers: cmsHeaders(env), + }, }, - ], - }, - server: { - port: 8080, - }, + }, + }; });