Skip to content

Commit

Permalink
feat: update translation and bug fix (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
younes200 authored Jan 14, 2025
1 parent 05aba8e commit 3cf9b95
Show file tree
Hide file tree
Showing 30 changed files with 961 additions and 455 deletions.
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
"@types/react-router-dom": "^4.3.1",
"@types/react-transition-group": "^2.0.9",
"@types/webpack-env": "^1.13.6",
"@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-react": "^4.1.0",
Expand Down
12 changes: 12 additions & 0 deletions apps/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ import ResetScroll from "./components/ResetScroll";
import commonEN from "./locales/en/common.json";
import commonES from "./locales/es/common.json";
import commonFR from "./locales/fr/common.json";
import commonHR from "./locales/hr/common.json";
import commonRO from "./locales/ro/common.json";
import commonID from "./locales/id/common.json";
import { About } from "./pages/about";
import { CreateProjectPage } from "./pages/create";
import { HomePage } from "./pages/home";
Expand Down Expand Up @@ -78,6 +81,15 @@ i18next
es_ES: {
translations: commonES,
},
hr_HR: {
translations: commonHR,
},
ro_RO: {
translations: commonRO,
},
id_ID: {
translations: commonID,
},
},
ns: ["translations"],
defaultNS: "translations",
Expand Down
9 changes: 9 additions & 0 deletions apps/frontend/src/components/LanguageMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ export const LanguageMenu = () => {
<MenuItem onClick={() => handleLangChange("es_ES")}>
{t("language-menu.spanish")}
</MenuItem>
<MenuItem onClick={() => handleLangChange("hr_HR")}>
{t("language.croatian")}
</MenuItem>
<MenuItem onClick={() => handleLangChange("ro_RO")}>
{t("language.romanian")}
</MenuItem>
<MenuItem onClick={() => handleLangChange("id_ID")}>
{t("language.indonesian")}
</MenuItem>
</Menu>
</React.Fragment>
);
Expand Down
6 changes: 3 additions & 3 deletions apps/frontend/src/components/annotation/AnnotationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ function AdvancedControls({
user?: UserMe;
}) {
const { mode, setMode } = usePlayerModeStore();

const { t } = useTranslation();
return (
<Stack
direction="row"
Expand All @@ -328,7 +328,7 @@ function AdvancedControls({
inputProps={{ "aria-label": "ant design" }}
/>
<Typography sx={{ color: "text.secondary", fontSize: "12px" }}>
Show Only Mine
{t("annotation.show-only-mine")}
</Typography>
</Stack>
<Stack direction="row" spacing={1} sx={{ alignItems: "center" }}>
Expand All @@ -340,7 +340,7 @@ function AdvancedControls({
inputProps={{ "aria-label": "ant design" }}
/>
<Typography sx={{ color: "text.secondary", fontSize: "12px" }}>
Performance Mode
{t("annotation.performance-mode")}
</Typography>
</Stack>
</Stack>
Expand Down
52 changes: 33 additions & 19 deletions apps/frontend/src/components/annotation/concept-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,55 @@ import FormControl from "@mui/material/FormControl";
import Select, { type SelectChangeEvent } from "@mui/material/Select";
import { useConceptsQuery } from "~utils/concepts";
import { grey } from "@mui/material/colors";
import { InputLabel, OutlinedInput } from "@mui/material";
import { useTranslation } from "react-i18next";

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

export function ConceptSelector() {
const { data } = useConceptsQuery();
const [concept, setConcept] = React.useState("");

const { t } = useTranslation();
const handleChange = (event: SelectChangeEvent) => {
setConcept(event.target.value);
};

return (
<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
<FormControl sx={{ m: 1, maxWidth: 150 }} size="small">
<Select
labelId="demo-select-small-label"
id="demo-select-small"
labelId="concept-select-label"
id="concept-select"
value={concept}
label={t("annotation.concept.label")}
hiddenLabel
onChange={handleChange}
sx={{
color: "white",
".MuiOutlinedInput-notchedOutline": {
borderColor: grey[800],
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: "white",
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "white",
},
".MuiSvgIcon-root": {
color: "white",
},
displayEmpty
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected === "") {
return (
<span style={{ color: "white" }}>
{t("annotation.concept.label")}
</span>
);
}
return <span style={{ color: "white" }}>{selected}</span>;
}}
MenuProps={MenuProps}
inputProps={{ "aria-label": "Without label" }}
>
<MenuItem value="">
<em>Sélectionner un concept</em>
<MenuItem disabled value="">
<em>{t("annotation.select-concept")}</em>
</MenuItem>
{data?.map((concept) => (
<MenuItem key={concept.concept} value={concept.concept}>
Expand Down
215 changes: 215 additions & 0 deletions apps/frontend/src/components/commun/button-duplin-export.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import * as React from "react";
import { styled, alpha } from "@mui/material/styles";
import Button from "@mui/material/Button";
import Menu, { type MenuProps } from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import type { ProjectById } from "~/utils/trpc";
import { parseString, Builder } from "xml2js";
import { useTranslation } from "react-i18next";
import saveAs from "file-saver";
import { useSnackbar } from "notistack";
import type { DublinMetadata } from "../project/dubin-panel";

const StyledMenu = styled((props: MenuProps) => (
<Menu
elevation={0}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
{...props}
/>
))(({ theme }) => ({
"& .MuiPaper-root": {
borderRadius: 6,
marginTop: theme.spacing(1),
minWidth: 180,
color: "rgb(55, 65, 81)",
boxShadow:
"rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px",
"& .MuiMenu-list": {
padding: "4px 0",
},
"& .MuiMenuItem-root": {
"& .MuiSvgIcon-root": {
fontSize: 18,
color: theme.palette.text.secondary,
marginRight: theme.spacing(1.5),
},
"&:active": {
backgroundColor: alpha(
theme.palette.primary.main,
theme.palette.action.selectedOpacity
),
},
},
...theme.applyStyles("dark", {
color: theme.palette.grey[300],
}),
},
}));

export default function DuplinExportButton({
project,
onImport,
}: {
project: ProjectById;
onImport: (metadata: DublinMetadata) => void;
}) {
const { t } = useTranslation();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const { enqueueSnackbar } = useSnackbar();

const handleClose = () => {
setAnchorEl(null);
};

// Save metadata to XML
const handleExport = () => {
handleClose();

const builder = new Builder({
xmldec: { version: "1.0", encoding: "UTF-8" },
});

const dublin = project.dublin;

const xmlObject = {
"rdf:RDF": {
$: {
"xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"xmlns:dcterms": "http://purl.org/dc/terms/",
},

"dcterms:title": [dublin?.title ?? ""],
"dcterms:creator": [dublin?.creator ?? ""],
"dcterms:subject": [dublin?.subject ?? ""],
"dcterms:description": [dublin?.description ?? ""],
"dcterms:publisher": [dublin?.publisher ?? ""],
"dcterms:contributor": [dublin?.contributor ?? ""],
"dcterms:date": [dublin?.date ?? ""],
"dcterms:type": [dublin?.type ?? ""],
"dcterms:format": [dublin?.format ?? ""],
"dcterms:identifier": [dublin?.identifier ?? ""],
"dcterms:source": [dublin?.source ?? ""],
"dcterms:language": [dublin?.language ?? ""],
"dcterms:relation": [dublin?.relation ?? ""],
"dcterms:coverage": [dublin?.coverage ?? ""],
"dcterms:rights": [dublin?.rights ?? ""],
},
};

const data = builder.buildObject(xmlObject);
// Simulate file save (in a real app, you'd use file system API)

const blob = new Blob([data], {
type: "text/xml;charset=utf-8",
});
saveAs(blob, "dublin.xml");

enqueueSnackbar("Exported successfully", {
variant: "success",
key: "metadata.dublin.export.success",
});
};

// Load XML file on component mount
const handleImport = async () => {
handleClose();
const [fileHandle] = await window.showOpenFilePicker({
types: [
{
description: "XML Files",
accept: {
"text/xml": [".xml"],
},
},
],
multiple: false,
});

// Get the file
const file = await fileHandle.getFile();
const text = await file.text();

// Parse XML to set initial metadata
parseString(text, (err, result) => {
if (err) {
console.error("Error parsing XML", err);
return;
}

const rdfData = result?.["rdf:RDF"] || {};
const newMetadata = {
title: rdfData["dcterms:title"]?.[0] ?? undefined,
creator: rdfData["dcterms:creator"]?.[0] ?? undefined,
subject: rdfData["dcterms:subject"]?.[0] ?? undefined,
description: rdfData["dcterms:description"]?.[0] ?? undefined,
publisher: rdfData["dcterms:publisher"]?.[0] ?? undefined,
contributor: rdfData["dcterms:contributor"]?.[0] ?? undefined,
date: rdfData["dcterms:date"]?.[0] ?? undefined,
type: rdfData["dcterms:type"]?.[0] ?? undefined,
format: rdfData["dcterms:format"]?.[0] ?? undefined,
identifier: rdfData["dcterms:identifier"]?.[0] ?? undefined,
source: rdfData["dcterms:source"]?.[0] ?? undefined,
language: rdfData["dcterms:language"]?.[0] ?? undefined,
relation: rdfData["dcterms:relation"]?.[0] ?? undefined,
coverage: rdfData["dcterms:coverage"]?.[0] ?? undefined,
rights: rdfData["dcterms:rights"]?.[0] ?? undefined,
};

const cleanMetadata = Object.fromEntries(
Object.entries(newMetadata).filter(
([_, value]) => value !== undefined && value !== null && value !== ""
)
);

onImport(cleanMetadata as DublinMetadata);
});
};

return (
<div>
<Button
id="demo-customized-button"
aria-controls={open ? "demo-customized-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
variant="contained"
disableElevation
onClick={handleClick}
endIcon={<KeyboardArrowDownIcon />}
>
{t("metadata.dublin.option")}
</Button>
<StyledMenu
id="demo-customized-menu"
MenuListProps={{
"aria-labelledby": "demo-customized-button",
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem onClick={handleExport} disableRipple>
{t("metadata.dublin.menu.export")}
</MenuItem>
{project.editable && (
<MenuItem onClick={handleImport} disableRipple>
{t("metadata.dublin.menu.import")}
</MenuItem>
)}
</StyledMenu>
</div>
);
}
Loading

0 comments on commit 3cf9b95

Please sign in to comment.