From 01cc6e8faf29d5980506636b833317baf78b0732 Mon Sep 17 00:00:00 2001 From: Henry Catalini Smith Date: Mon, 27 May 2024 21:34:14 +0200 Subject: [PATCH] Create project page --- .../src/app/projects/[projectName]/page.tsx | 51 ++++++++++ webapp/src/components/CardGrid.tsx | 44 +++++++++ webapp/src/components/HomeDashboard.tsx | 34 +++---- webapp/src/components/LanguageCard.tsx | 99 +++++++++++++++++++ webapp/src/components/ProjectDashboard.tsx | 77 +++++++++++++++ 5 files changed, 283 insertions(+), 22 deletions(-) create mode 100644 webapp/src/app/projects/[projectName]/page.tsx create mode 100644 webapp/src/components/CardGrid.tsx create mode 100644 webapp/src/components/LanguageCard.tsx create mode 100644 webapp/src/components/ProjectDashboard.tsx diff --git a/webapp/src/app/projects/[projectName]/page.tsx b/webapp/src/app/projects/[projectName]/page.tsx new file mode 100644 index 00000000..26365a1e --- /dev/null +++ b/webapp/src/app/projects/[projectName]/page.tsx @@ -0,0 +1,51 @@ +import { Cache } from '@/Cache'; +import MessageAdapterFactory from '@/utils/adapters/MessageAdapterFactory'; +import { NextPage } from 'next'; +import { notFound } from 'next/navigation'; +import ProjectDashboard from '@/components/ProjectDashboard'; +import { RepoGit } from '@/RepoGit'; +import { ServerConfig } from '@/utils/serverConfig'; + +const ProjectPage: NextPage<{ + params: { projectName: string }; +}> = async ({ params }) => { + const serverConfig = await ServerConfig.read(); + const project = serverConfig.projects.find( + (project) => project.name === params.projectName, + ); + + if (!project) { + return notFound(); + } + + await RepoGit.cloneIfNotExist(project); + const repoGit = await RepoGit.getRepoGit(project); + const lyraConfig = await repoGit.getLyraConfig(); + const projectConfig = lyraConfig.getProjectConfigByPath(project.projectPath); + const msgAdapter = MessageAdapterFactory.createAdapter(projectConfig); + const messages = await msgAdapter.getMessages(); + const store = await Cache.getProjectStore(projectConfig); + const languages = await Promise.all( + projectConfig.languages.map(async (lang) => { + const translations = await store.getTranslations(lang); + return { + href: `/projects/${project.name}/${lang}`, + language: lang, + messagesLeft: messages.length - Object.keys(translations).length, + progress: translations + ? (Object.keys(translations).length / messages.length) * 100 + : 0, + }; + }), + ); + + return ( + + ); +}; + +export default ProjectPage; diff --git a/webapp/src/components/CardGrid.tsx b/webapp/src/components/CardGrid.tsx new file mode 100644 index 00000000..20d96d02 --- /dev/null +++ b/webapp/src/components/CardGrid.tsx @@ -0,0 +1,44 @@ +import { Box, List } from '@mui/joy'; +import { FC, ReactNode } from 'react'; + +export type CardGridProps = { + children: ReactNode; + heading?: ReactNode; +}; + +export const CardGrid: FC = ({ children, heading }) => { + return ( + + {heading && {heading}} + + {children} + + + ); +}; diff --git a/webapp/src/components/HomeDashboard.tsx b/webapp/src/components/HomeDashboard.tsx index f78311b2..5c57d13c 100644 --- a/webapp/src/components/HomeDashboard.tsx +++ b/webapp/src/components/HomeDashboard.tsx @@ -1,6 +1,7 @@ +import { CardGrid } from '@/components/CardGrid'; import { FC } from 'react'; -import { Box, List, Typography } from '@mui/joy'; -import ProjectCard, { ProjectCardProps } from './ProjectCard'; +import { Box, Typography } from '@mui/joy'; +import ProjectCard, { ProjectCardProps } from '@/components/ProjectCard'; type HomeDashboardProps = { projects: ProjectCardProps[]; @@ -15,30 +16,19 @@ const HomeDashboard: FC = ({ projects }) => { justifyContent="center" minHeight="97vh" > - - Your Lyra Projects - - + + Your Lyra Projects + + + } > {projects.map((project, i) => ( ))} - + ); }; diff --git a/webapp/src/components/LanguageCard.tsx b/webapp/src/components/LanguageCard.tsx new file mode 100644 index 00000000..1df3a24b --- /dev/null +++ b/webapp/src/components/LanguageCard.tsx @@ -0,0 +1,99 @@ +import { FC } from 'react'; +import { Box, LinearProgress, Link, Typography } from '@mui/joy'; + +export type LanguageCardProps = { + /** + * The URL of the page containing the project's messages in this language. + */ + + href: string; + + /** + * The name of the language. + */ + language: string; + + /** + * The number of messages left to translate in this language. + */ + messagesLeft: number; + + /** + * The percentage of messages translated in this language. 0 means none, 100 + * means all of them. + */ + progress: number; +}; + +/** + * A language card can be clicked to navigate to the page containing all a + * project's messages in that language. It display's the name of the language + * and some brief statistics about how complete the translation is. + */ +const LanguageCard: FC = ({ + href, + language, + messagesLeft, + progress, +}) => { + return ( + + + + + {language} + + {' '} + + + {messagesLeft} messages to translate + + + ); +}; + +export default LanguageCard; diff --git a/webapp/src/components/ProjectDashboard.tsx b/webapp/src/components/ProjectDashboard.tsx new file mode 100644 index 00000000..9a344cbd --- /dev/null +++ b/webapp/src/components/ProjectDashboard.tsx @@ -0,0 +1,77 @@ +'use client'; + +import { CardGrid } from '@/components/CardGrid'; +import { FC } from 'react'; +import Link from 'next/link'; +import { Box, Typography, useTheme } from '@mui/joy'; +import LanguageCard, { LanguageCardProps } from '@/components/LanguageCard'; + +type ProjectDashboardProps = { + languages: LanguageCardProps[]; + messageCount: number; + project: string; +}; + +const ProjectDashboard: FC = ({ + languages, + messageCount, + project, +}) => { + const theme = useTheme(); + return ( + + + + + Home + + + + + + + {project} + {messageCount} messages + + } + > + {languages.map((language, i) => ( + + ))} + + + + ); +}; + +export default ProjectDashboard;