From e69736b70a09614bb1305682d92682b4932e94d7 Mon Sep 17 00:00:00 2001 From: Andres Galindo Date: Mon, 22 Jul 2024 11:45:17 -0700 Subject: [PATCH 01/16] Enhancment/Grant app permissions to new system roles (#2863) --- src/shell/store/products.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shell/store/products.js b/src/shell/store/products.js index 6b5b9befd3..c0e5fece04 100644 --- a/src/shell/store/products.js +++ b/src/shell/store/products.js @@ -20,6 +20,7 @@ export function fetchProducts() { // seo: 31-71cfc74-s30 case "31-71cfc74-0wn3r": case "31-71cfc74-4dm13": + case "31-71cfc74-4cc4dm13": data = [ "launchpad", "content", @@ -35,6 +36,7 @@ export function fetchProducts() { ]; break; case "31-71cfc74-d3v3l0p3r": + case "31-71cfc74-d3vc0n": data = [ "launchpad", "content", From 7398833b8b1839c813261cfaf86365433bd051cd Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:15:51 +0800 Subject: [PATCH 02/16] [Content] Do not show edit template for datasets (#2870) Fixes #2823 ### Demo ![Screenshot 2024-07-23 105044](https://github.com/user-attachments/assets/e59e973d-75f8-4197-8c7e-48d2be3b5163) --- .../app/views/ItemList/ItemListActions.tsx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx index afb11d870b..ff38b2eff9 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx @@ -178,17 +178,19 @@ export const ItemListActions = forwardRef((props, ref) => { Edit Model - { - history.push(codePath); - }} - > - - - - Edit Template - + {!isDataset && ( + { + history.push(codePath); + }} + > + + + + Edit Template + + )} Date: Mon, 22 Jul 2024 22:38:26 -0700 Subject: [PATCH 03/16] Schema rules limit file type for media field (#2867) --- .../src/app/components/Editor/Field/Field.tsx | 1 + .../src/app/components/FieldTypeMedia.tsx | 116 ++++++- .../AddFieldModal/FieldFormInput.tsx | 4 +- .../components/AddFieldModal/MediaRules.tsx | 284 +++++++++++++++++- .../AddFieldModal/views/FieldForm.tsx | 37 ++- .../components/AddFieldModal/views/Rules.tsx | 3 + src/apps/schema/src/app/components/configs.ts | 14 + src/shell/services/types.ts | 3 + 8 files changed, 443 insertions(+), 19 deletions(-) diff --git a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx index 886b96dc93..d9a42aa90e 100644 --- a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx +++ b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx @@ -482,6 +482,7 @@ export const Field = ({ ), }); }} + settings={settings} name={name} onChange={onChange} lockedToGroupId={ diff --git a/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx b/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx index 6ea4f8af00..bec4d47bd3 100644 --- a/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx +++ b/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx @@ -53,6 +53,7 @@ type FieldTypeMediaProps = { hasError?: boolean; hideDrag?: boolean; lockedToGroupId: string | null; + settings?: any; }; export const FieldTypeMedia = ({ @@ -64,6 +65,7 @@ export const FieldTypeMedia = ({ hasError, hideDrag, lockedToGroupId, + settings, }: FieldTypeMediaProps) => { const [draggedIndex, setDraggedIndex] = useState(null); const [hoveredIndex, setHoveredIndex] = useState(null); @@ -77,6 +79,7 @@ export const FieldTypeMedia = ({ const [imageToReplace, setImageToReplace] = useState(""); const [isBynderOpen, setIsBynderOpen] = useState(false); const { data: rawInstanceSettings } = useGetInstanceSettingsQuery(); + const [selectionError, setSelectionError] = useState(""); const bynderPortalUrlSetting = rawInstanceSettings?.find( (setting) => setting.key === "bynder_portal_url" @@ -109,26 +112,92 @@ export const FieldTypeMedia = ({ }, [bynderTokenSetting]); const addZestyImage = (selectedImages: any[]) => { - const newImageZUIDs = selectedImages.map((image) => image.id); + const removedImages: any[] = []; + const filteredSelectedImages = selectedImages?.filter((selectedImage) => { + //remove any images that do not match the file extension + if (settings?.fileExtensions) { + if ( + settings?.fileExtensions?.includes( + `.${fileExtension(selectedImage.filename)}` + ) + ) { + return true; + } else { + removedImages.push(selectedImage); + return false; + } + } else { + return true; + } + }); + + if (removedImages.length) { + const filenames = removedImages.map((image) => image.filename); + const formattedFilenames = + filenames.length > 1 + ? filenames.slice(0, -1).join(", ") + " and " + filenames.slice(-1) + : filenames[0]; + + setSelectionError( + `Could not add ${formattedFilenames}. ${settings?.fileExtensionsErrorMessage}` + ); + } else { + setSelectionError(""); + } + + const newImageZUIDs = filteredSelectedImages?.map((image) => image.id); + // remove any duplicates const filteredImageZUIDs = newImageZUIDs.filter( (zuid) => !images.includes(zuid) ); + // Do not trigger onChange if no images are added + if (![...images, ...filteredImageZUIDs]?.length) return; + onChange([...images, ...filteredImageZUIDs].join(","), name); }; const addBynderAsset = (selectedAsset: any[]) => { if (images.length > limit) return; - const newBynderAssets = selectedAsset + const removedAssets: any[] = []; + const filteredBynderAssets = selectedAsset?.filter((asset) => { + if (settings?.fileExtensions) { + const assetExtension = `.${asset.extensions[0]}`; + if (settings?.fileExtensions?.includes(assetExtension)) { + return true; + } else { + removedAssets.push(asset); + return false; + } + } else { + return true; + } + }); + + if (removedAssets.length) { + const filenames = removedAssets.map((asset) => asset.name); + const formattedFilenames = + filenames.length > 1 + ? filenames.slice(0, -1).join(", ") + " and " + filenames.slice(-1) + : filenames[0]; + + setSelectionError( + `Could not add ${formattedFilenames}. ${settings?.fileExtensionsErrorMessage}` + ); + } else { + setSelectionError(""); + } + + const newBynderAssets = filteredBynderAssets .slice(0, limit - images.length) .map((asset) => asset.originalUrl); - const filteredBynderAssets = newBynderAssets.filter( + const filteredBynderAssetsUrls = newBynderAssets.filter( (asset) => !images.includes(asset) ); - onChange([...images, ...filteredBynderAssets].join(","), name); + onChange([...images, ...filteredBynderAssetsUrls].join(","), name); }; const removeImage = (imageId: string) => { @@ -146,6 +215,21 @@ export const FieldTypeMedia = ({ }); // if selected replacement image is already in the list of images, do nothing if (localImageZUIDs.includes(imageZUID)) return; + // if extension is not allowed set error message + if (settings?.fileExtensions) { + if ( + !settings?.fileExtensions?.includes( + `.${fileExtension(images[0].filename)}` + ) + ) { + setSelectionError( + `Could not replace. ${settings?.fileExtensionsErrorMessage}` + ); + return; + } else { + setSelectionError(""); + } + } const newImageZUIDs = localImageZUIDs.map((zuid) => { if (zuid === imageToReplace) { return imageZUID; @@ -153,6 +237,7 @@ export const FieldTypeMedia = ({ return zuid; }); + onChange(newImageZUIDs.join(","), name); }; @@ -160,6 +245,19 @@ export const FieldTypeMedia = ({ // Prevent adding bynder asset that has already been added if (localImageZUIDs.includes(selectedAsset.originalUrl)) return; + const assetExtension = `.${selectedAsset.extensions[0]}`; + if ( + settings?.fileExtensions && + !settings?.fileExtensions?.includes(assetExtension) + ) { + setSelectionError( + `Could not replace. ${settings?.fileExtensionsErrorMessage}` + ); + return; + } else { + setSelectionError(""); + } + const newImages = localImageZUIDs.map((image) => { if (image === imageToReplace) { return selectedAsset.originalUrl; @@ -323,6 +421,11 @@ export const FieldTypeMedia = ({ )} + {selectionError && ( + + {selectionError} + + )} setIsBynderOpen(false)}> @@ -421,6 +524,11 @@ export const FieldTypeMedia = ({ )} + {selectionError && ( + + {selectionError} + + )} {showFileModal && ( ; -type MediaFieldName = Extract; const MediaLabelsConfig: { [key in MediaFieldName]: { label: string; subLabel: string }; } = { @@ -24,8 +32,83 @@ const MediaLabelsConfig: { label: "Lock to a folder", subLabel: "Ensures files can only be selected from a specific folder", }, + fileExtensions: { + label: "Limit File Types", + subLabel: "Ensures only certain file types can be accepted", + }, }; +const ExtensionPresets = [ + { + label: "Images", + value: [".png", ".jpg", ".jpeg", ".svg", ".gif", ".tif", ".webp"], + }, + { + label: "Videos", + value: [ + ".mob", + ".avi", + ".wmv", + ".mp4", + ".mpeg", + ".mkv", + ".m4v", + ".mpg", + ".webm", + ], + }, + { + label: "Audios", + value: [ + ".mp3", + ".flac", + ".wav", + ".m4a", + ".aac", + ".ape", + ".opus", + ".aiff", + ".aif", + ], + }, + { + label: "Documents", + value: [".doc", ".pdf", ".docx", ".txt", ".rtf", ".odt", ".pages"], + }, + { + label: "Presentations", + value: [ + ".ppt", + ".pptx", + ".key", + ".odp", + ".pps", + ".ppsx", + ".sldx", + ".potx", + ".otp", + ".sxi", + ], + }, + { + label: "Spreadsheets", + value: [ + ".xls", + ".xlsx", + ".csv", + ".tsv", + ".numbers", + ".ods", + ".xlsm", + ".xlsb", + ".xlt", + ".xltx", + ], + }, +] as const; + +const RestrictedExtensions = [".exe", ".dmg"]; + interface Props { fieldConfig: InputField[]; groups: CustomGroup[]; @@ -37,13 +120,73 @@ interface Props { value: FormValue; }) => void; fieldData: { [key: string]: FormValue }; + errors: Errors; } + export const MediaRules = ({ fieldConfig, onDataChange, groups, fieldData, + errors, }: Props) => { + const [inputValue, setInputValue] = useState(""); + const [autoFill, setAutoFill] = useState( + !fieldData.fileExtensionsErrorMessage + ); + const [extensionsError, setExtensionsError] = useState(false); + + useEffect(() => { + if (autoFill) { + onDataChange({ + inputName: "fileExtensionsErrorMessage", + value: + "Only files with the following extensions are allowed: " + + (fieldData["fileExtensions"] as string[])?.join(", "), + }); + } + }, [autoFill, fieldData["fileExtensions"]]); + + const handleInputChange = ( + event: any, + newInputValue: string, + ruleName: string + ) => { + const formattedInput = "." + newInputValue.replace(/\./g, ""); + setInputValue(formattedInput); + }; + + const handleKeyDown = (event: any, ruleName: string) => { + if (event.key === "Enter" || event.key === "," || event.key === " ") { + event.preventDefault(); + const newOption = inputValue.toLowerCase().trim(); + if ( + newOption && + !(fieldData[ruleName] as string[]).includes(newOption) && + !RestrictedExtensions.includes(newOption) + ) { + onDataChange({ + inputName: ruleName, + value: [...(fieldData[ruleName] as string[]), newOption], + }); + setInputValue(""); + } + } + }; + + const handleDelete = (option: string, ruleName: string) => { + const newTags = (fieldData[ruleName] as string[]).filter( + (item) => item !== option + ); + if (!newTags.length) { + setExtensionsError(true); + } + onDataChange({ + inputName: ruleName, + value: newTags, + }); + }; + return ( {fieldConfig?.map((rule: InputField, key: number) => { - if (rule.name === "defaultValue") return; + if ( + rule.name === "defaultValue" || + rule.name === "fileExtensionsErrorMessage" + ) + return null; return ( @@ -101,7 +257,9 @@ export const MediaRules = ({ } /> - {Boolean(fieldData[rule.name]) && ( + {Boolean( + fieldData[rule.name] && rule.name !== "fileExtensions" + ) && ( )} + + {Boolean(fieldData[rule.name]) && rule.name === "fileExtensions" && ( + + Extensions * + ( + handleKeyDown(event, rule.name)} + /> + )} + onInputChange={(event, newInputValue) => + handleInputChange(event, newInputValue, rule.name) + } + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + handleDelete(option, rule.name)} + clickable={false} + sx={{ + backgroundColor: "common.white", + borderColor: "grey.300", + borderWidth: 1, + borderStyle: "solid", + }} + /> + )) + } + /> + {errors["fileExtensions"] && extensionsError && ( + + {errors["fileExtensions"]} + + )} + + Add: + {ExtensionPresets.map((preset) => ( + { + const newTags = fieldData[rule.name] as string[]; + const tags = new Set(newTags); + preset.value.forEach((tag) => tags.add(tag)); + onDataChange({ + inputName: rule.name, + value: Array.from(tags), + }); + }} + sx={{ + backgroundColor: "common.white", + borderColor: "grey.300", + borderWidth: 1, + borderStyle: "solid", + }} + /> + ))} + + + Custom Error Message * + + { + setAutoFill(false); + onDataChange({ + inputName: "fileExtensionsErrorMessage", + value: e.target.value, + }); + }} + /> + {errors["fileExtensionsErrorMessage"] && ( + + {errors["fileExtensionsErrorMessage"]} + + )} + + )} ); })} diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx index c315404b73..810467613a 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx @@ -223,6 +223,10 @@ export const FieldForm = ({ formFields[field.name] = fieldData.settings[field.name] ?? null; } else if (field.name === "maxValue") { formFields[field.name] = fieldData.settings[field.name] ?? null; + } else if (field.name === "fileExtensions") { + formFields[field.name] = fieldData.settings[field.name] ?? null; + } else if (field.name === "fileExtensionsErrorMessage") { + formFields[field.name] = fieldData.settings[field.name] ?? null; } else { formFields[field.name] = fieldData[field.name] as FormValue; } @@ -249,7 +253,9 @@ export const FieldForm = ({ field.name === "regexRestrictPattern" || field.name === "regexRestrictErrorMessage" || field.name === "minValue" || - field.name === "maxValue" + field.name === "maxValue" || + field.name === "fileExtensions" || + field.name === "fileExtensionsErrorMessage" ) { formFields[field.name] = null; } else { @@ -393,6 +399,22 @@ export const FieldForm = ({ } } + if ( + inputName === "fileExtensions" && + formData.fileExtensions !== null && + !(formData.fileExtensions as string[])?.length + ) { + newErrorsObj[inputName] = "This field is required"; + } + + if ( + inputName === "fileExtensionsErrorMessage" && + formData.fileExtensions !== null && + formData.fileExtensionsErrorMessage === "" + ) { + newErrorsObj[inputName] = "This field is required"; + } + if ( inputName in errors && ![ @@ -405,6 +427,8 @@ export const FieldForm = ({ "regexRestrictErrorMessage", "minValue", "maxValue", + "fileExtensions", + "fileExtensionsErrorMessage", ].includes(inputName) ) { const { maxLength, label, validate } = FORM_CONFIG[type].details.find( @@ -508,7 +532,9 @@ export const FieldForm = ({ errors.regexRestrictPattern || errors.regexRestrictErrorMessage || errors.minValue || - errors.maxValue + errors.maxValue || + errors.fileExtensions || + errors.fileExtensionsErrorMessage ) { setActiveTab("rules"); } else { @@ -562,6 +588,13 @@ export const FieldForm = ({ ...(formData.maxValue !== null && { maxValue: formData.maxValue as number, }), + ...(formData.fileExtensions && { + fileExtensions: formData.fileExtensions as string[], + }), + ...(formData.fileExtensionsErrorMessage && { + fileExtensionsErrorMessage: + formData.fileExtensionsErrorMessage as string, + }), }, sort: isUpdateField ? fieldData.sort : sort, // Just use the length since sort starts at 0 }; diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx index 8c7d473707..23c146ffc8 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx @@ -50,12 +50,15 @@ export const Rules = ({ {type === "images" && ( )} diff --git a/src/apps/schema/src/app/components/configs.ts b/src/apps/schema/src/app/components/configs.ts index 5854dded08..ec60c38746 100644 --- a/src/apps/schema/src/app/components/configs.ts +++ b/src/apps/schema/src/app/components/configs.ts @@ -556,6 +556,20 @@ const FORM_CONFIG: Record = { required: false, gridSize: 12, }, + { + name: "fileExtensions", + type: "input", + label: "File Extensions", + required: false, + gridSize: 12, + }, + { + name: "fileExtensionsErrorMessage", + type: "input", + label: "File extensions error message", + required: false, + gridSize: 12, + }, ...COMMON_RULES, ], }, diff --git a/src/shell/services/types.ts b/src/shell/services/types.ts index 62c7cd1aac..b31e07a159 100644 --- a/src/shell/services/types.ts +++ b/src/shell/services/types.ts @@ -203,12 +203,15 @@ export interface FieldSettings { regexRestrictErrorMessage?: string; minValue?: number; maxValue?: number; + fileExtensions?: string[]; + fileExtensionsErrorMessage?: string; } export type ContentModelFieldValue = | string | number | boolean + | string[] | FieldSettings | FieldSettingsOptions[]; From 4d91e56f2e78e80deac506dee31616b27aae8455 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:00:05 +0800 Subject: [PATCH 04/16] [Content] Do not show the model as a breadcrumb item when in multipage table view (#2861) Resolves #2829 ### Demo https://github.com/user-attachments/assets/24cb4229-e209-460e-a765-639e7cbc7a24 --------- Co-authored-by: Stuart Runyan --- .../src/app/components/ContentBreadcrumbs.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx b/src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx index 3d4dcbd0ab..dfedaa2610 100644 --- a/src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx +++ b/src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx @@ -4,7 +4,7 @@ import { useGetContentNavItemsQuery, } from "../../../../../shell/services/instance"; import { Home } from "@zesty-io/material"; -import { useHistory, useParams } from "react-router"; +import { useHistory, useParams, useLocation } from "react-router"; import { useMemo } from "react"; import { ContentNavItem } from "../../../../../shell/services/types"; import { MODEL_ICON } from "../../../../../shell/constants"; @@ -18,8 +18,12 @@ export const ContentBreadcrumbs = () => { itemZUID: string; }>(); const history = useHistory(); + const location = useLocation(); const breadcrumbData = useMemo(() => { + const isInMultipageTableView = !["new", "import"].includes( + location?.pathname?.split("/")?.pop() + ); let activeItem: ContentNavItem; const crumbs = []; @@ -52,6 +56,12 @@ export const ContentBreadcrumbs = () => { parent = null; } } + + if (!itemZUID && isInMultipageTableView) { + // Remove the model as a breadcrumb item when viewing in multipage table view + crumbs?.pop(); + } + return crumbs.map((item) => ({ node: , onClick: () => { @@ -62,7 +72,7 @@ export const ContentBreadcrumbs = () => { } }, })); - }, [nav, itemZUID]); + }, [nav, itemZUID, modelZUID, location]); return ( Date: Tue, 23 Jul 2024 12:37:38 -0700 Subject: [PATCH 05/16] Fix/table sort by display date field by accounting for empty date values (#2864) Co-authored-by: Stuart Runyan --- .../content-editor/src/app/views/ItemList/index.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/apps/content-editor/src/app/views/ItemList/index.tsx b/src/apps/content-editor/src/app/views/ItemList/index.tsx index 8e572918f7..f3ee800eaa 100644 --- a/src/apps/content-editor/src/app/views/ItemList/index.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/index.tsx @@ -334,9 +334,16 @@ export const ItemList = () => { : b.data[sort] - a.data[sort]; } if (dataType === "date" || dataType === "datetime") { - return ( - new Date(b.data[sort]).getTime() - new Date(a.data[sort]).getTime() - ); + if (!a.data[sort]) { + return 1; + } else if (!b.data[sort]) { + return -1; + } else { + return ( + new Date(b.data[sort]).getTime() - + new Date(a.data[sort]).getTime() + ); + } } const aValue = dataType === "images" ? a.data[sort]?.filename : a.data[sort]; From b9acd9968a7dc209a842246b2c4c9163410b101d Mon Sep 17 00:00:00 2001 From: Andres Galindo Date: Wed, 24 Jul 2024 13:53:13 -0700 Subject: [PATCH 06/16] Reference createdAt timestamp outside of history object (#2868) --- src/shell/services/marketing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/services/marketing.ts b/src/shell/services/marketing.ts index 3a0f367010..a23b553684 100644 --- a/src/shell/services/marketing.ts +++ b/src/shell/services/marketing.ts @@ -43,7 +43,7 @@ export const marketingApi = createApi({ video_link, start_date_and_time, end_date_and_time, - created_at: currVal?.version?.history?.data?.pop()?.createdAt, + created_at: currVal?.version?.createdAt, }, ]; } From 172758193909be7ff8d0fd9dde214f538f10fd62 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:00:56 +0800 Subject: [PATCH 07/16] [Schema | Content] Currency field revamp (#2873) - Allows users to select the default currency at a model level - Improved currency selection - Show selected currency in the content item Resolves #2455 Resolves #2454 Resolves #2790 ### Demo ![Screenshot 2024-07-24 080146](https://github.com/user-attachments/assets/411a7343-221e-46bf-9460-9457f38abcd0) ![Screenshot 2024-07-24 080228](https://github.com/user-attachments/assets/28e7ce4d-2f64-437b-8e47-60e6dceb565a) ![Screenshot 2024-07-24 080405](https://github.com/user-attachments/assets/13c66db9-bebf-4058-9024-88fe88abfd43) --- cypress/e2e/content/content.spec.js | 2 +- cypress/e2e/schema/field.spec.js | 40 + public/images/flags/ad.svg | 150 ++++ public/images/flags/ae.svg | 6 + public/images/flags/af.svg | 81 ++ public/images/flags/ag.svg | 14 + public/images/flags/ai.svg | 29 + public/images/flags/al.svg | 5 + public/images/flags/am.svg | 5 + public/images/flags/ao.svg | 13 + public/images/flags/aq.svg | 5 + public/images/flags/ar.svg | 32 + public/images/flags/arab.svg | 109 +++ public/images/flags/as.svg | 72 ++ public/images/flags/at.svg | 4 + public/images/flags/au.svg | 8 + public/images/flags/aw.svg | 186 ++++ public/images/flags/ax.svg | 18 + public/images/flags/az.svg | 8 + public/images/flags/ba.svg | 12 + public/images/flags/bb.svg | 6 + public/images/flags/bd.svg | 4 + public/images/flags/be.svg | 7 + public/images/flags/bf.svg | 7 + public/images/flags/bg.svg | 5 + public/images/flags/bh.svg | 4 + public/images/flags/bi.svg | 15 + public/images/flags/bj.svg | 14 + public/images/flags/bl.svg | 5 + public/images/flags/bm.svg | 97 ++ public/images/flags/bn.svg | 36 + public/images/flags/bo.svg | 674 ++++++++++++++ public/images/flags/bq.svg | 5 + public/images/flags/br.svg | 45 + public/images/flags/bs.svg | 13 + public/images/flags/bt.svg | 89 ++ public/images/flags/bv.svg | 13 + public/images/flags/bw.svg | 7 + public/images/flags/by.svg | 18 + public/images/flags/bz.svg | 145 +++ public/images/flags/ca.svg | 4 + public/images/flags/cc.svg | 19 + public/images/flags/cd.svg | 5 + public/images/flags/cefta.svg | 13 + public/images/flags/cf.svg | 15 + public/images/flags/cg.svg | 12 + public/images/flags/ch.svg | 9 + public/images/flags/ci.svg | 7 + public/images/flags/ck.svg | 9 + public/images/flags/cl.svg | 13 + public/images/flags/cm.svg | 15 + public/images/flags/cn.svg | 11 + public/images/flags/co.svg | 7 + public/images/flags/cp.svg | 7 + public/images/flags/cr.svg | 7 + public/images/flags/cu.svg | 13 + public/images/flags/cv.svg | 13 + public/images/flags/cw.svg | 14 + public/images/flags/cx.svg | 15 + public/images/flags/cy.svg | 6 + public/images/flags/cz.svg | 5 + public/images/flags/de.svg | 5 + public/images/flags/dg.svg | 130 +++ public/images/flags/dj.svg | 13 + public/images/flags/dk.svg | 5 + public/images/flags/dm.svg | 152 ++++ public/images/flags/do.svg | 121 +++ public/images/flags/dz.svg | 5 + public/images/flags/eac.svg | 48 + public/images/flags/ec.svg | 138 +++ public/images/flags/ee.svg | 5 + public/images/flags/eg.svg | 38 + public/images/flags/eh.svg | 16 + public/images/flags/er.svg | 8 + public/images/flags/es-ct.svg | 4 + public/images/flags/es-ga.svg | 187 ++++ public/images/flags/es-pv.svg | 5 + public/images/flags/es.svg | 544 +++++++++++ public/images/flags/et.svg | 14 + public/images/flags/eu.svg | 28 + public/images/flags/fi.svg | 5 + public/images/flags/fj.svg | 120 +++ public/images/flags/fk.svg | 90 ++ public/images/flags/fm.svg | 11 + public/images/flags/fo.svg | 12 + public/images/flags/fr.svg | 5 + public/images/flags/ga.svg | 7 + public/images/flags/gb-eng.svg | 5 + public/images/flags/gb-nir.svg | 132 +++ public/images/flags/gb-sct.svg | 4 + public/images/flags/gb-wls.svg | 9 + public/images/flags/gb.svg | 7 + public/images/flags/gd.svg | 27 + public/images/flags/ge.svg | 6 + public/images/flags/gf.svg | 5 + public/images/flags/gg.svg | 9 + public/images/flags/gh.svg | 6 + public/images/flags/gi.svg | 32 + public/images/flags/gl.svg | 4 + public/images/flags/gm.svg | 14 + public/images/flags/gn.svg | 7 + public/images/flags/gp.svg | 5 + public/images/flags/gq.svg | 23 + public/images/flags/gr.svg | 16 + public/images/flags/gs.svg | 133 +++ public/images/flags/gt.svg | 204 +++++ public/images/flags/gu.svg | 23 + public/images/flags/gw.svg | 13 + public/images/flags/gy.svg | 9 + public/images/flags/hk.svg | 8 + public/images/flags/hm.svg | 8 + public/images/flags/hn.svg | 18 + public/images/flags/hr.svg | 58 ++ public/images/flags/ht.svg | 116 +++ public/images/flags/hu.svg | 7 + public/images/flags/ic.svg | 7 + public/images/flags/id.svg | 4 + public/images/flags/ie.svg | 7 + public/images/flags/il.svg | 14 + public/images/flags/im.svg | 36 + public/images/flags/in.svg | 25 + public/images/flags/io.svg | 130 +++ public/images/flags/iq.svg | 10 + public/images/flags/ir.svg | 219 +++++ public/images/flags/is.svg | 12 + public/images/flags/it.svg | 7 + public/images/flags/je.svg | 62 ++ public/images/flags/jm.svg | 8 + public/images/flags/jo.svg | 16 + public/images/flags/jp.svg | 11 + public/images/flags/ke.svg | 23 + public/images/flags/kg.svg | 15 + public/images/flags/kh.svg | 61 ++ public/images/flags/ki.svg | 36 + public/images/flags/km.svg | 16 + public/images/flags/kn.svg | 14 + public/images/flags/kp.svg | 15 + public/images/flags/kr.svg | 24 + public/images/flags/kw.svg | 13 + public/images/flags/ky.svg | 103 +++ public/images/flags/kz.svg | 36 + public/images/flags/la.svg | 12 + public/images/flags/lb.svg | 15 + public/images/flags/lc.svg | 8 + public/images/flags/li.svg | 43 + public/images/flags/lk.svg | 22 + public/images/flags/lr.svg | 14 + public/images/flags/ls.svg | 8 + public/images/flags/lt.svg | 7 + public/images/flags/lu.svg | 5 + public/images/flags/lv.svg | 6 + public/images/flags/ly.svg | 13 + public/images/flags/ma.svg | 4 + public/images/flags/mc.svg | 6 + public/images/flags/md.svg | 70 ++ public/images/flags/me.svg | 116 +++ public/images/flags/mf.svg | 5 + public/images/flags/mg.svg | 7 + public/images/flags/mh.svg | 7 + public/images/flags/mk.svg | 5 + public/images/flags/ml.svg | 7 + public/images/flags/mm.svg | 12 + public/images/flags/mn.svg | 14 + public/images/flags/mo.svg | 9 + public/images/flags/mp.svg | 86 ++ public/images/flags/mq.svg | 5 + public/images/flags/mr.svg | 6 + public/images/flags/ms.svg | 29 + public/images/flags/mt.svg | 58 ++ public/images/flags/mu.svg | 8 + public/images/flags/mv.svg | 6 + public/images/flags/mw.svg | 10 + public/images/flags/mx.svg | 382 ++++++++ public/images/flags/my.svg | 26 + public/images/flags/mz.svg | 21 + public/images/flags/na.svg | 16 + public/images/flags/nc.svg | 13 + public/images/flags/ne.svg | 6 + public/images/flags/nf.svg | 9 + public/images/flags/ng.svg | 6 + public/images/flags/ni.svg | 129 +++ public/images/flags/nl.svg | 5 + public/images/flags/no.svg | 7 + public/images/flags/np.svg | 13 + public/images/flags/nr.svg | 12 + public/images/flags/nu.svg | 10 + public/images/flags/nz.svg | 36 + public/images/flags/om.svg | 115 +++ public/images/flags/pa.svg | 14 + public/images/flags/pc.svg | 33 + public/images/flags/pe.svg | 4 + public/images/flags/pf.svg | 19 + public/images/flags/pg.svg | 9 + public/images/flags/ph.svg | 6 + public/images/flags/pk.svg | 15 + public/images/flags/pl.svg | 6 + public/images/flags/pm.svg | 5 + public/images/flags/pn.svg | 53 ++ public/images/flags/pr.svg | 13 + public/images/flags/ps.svg | 15 + public/images/flags/pt.svg | 57 ++ public/images/flags/pw.svg | 11 + public/images/flags/py.svg | 157 ++++ public/images/flags/qa.svg | 4 + public/images/flags/re.svg | 5 + public/images/flags/ro.svg | 7 + public/images/flags/rs.svg | 292 ++++++ public/images/flags/ru.svg | 5 + public/images/flags/rw.svg | 13 + public/images/flags/sa.svg | 25 + public/images/flags/sb.svg | 13 + public/images/flags/sc.svg | 7 + public/images/flags/sd.svg | 13 + public/images/flags/se.svg | 4 + public/images/flags/sg.svg | 13 + public/images/flags/sh-ac.svg | 689 ++++++++++++++ public/images/flags/sh-hl.svg | 164 ++++ public/images/flags/sh-ta.svg | 76 ++ public/images/flags/sh.svg | 7 + public/images/flags/si.svg | 18 + public/images/flags/sj.svg | 7 + public/images/flags/sk.svg | 9 + public/images/flags/sl.svg | 7 + public/images/flags/sm.svg | 75 ++ public/images/flags/sn.svg | 8 + public/images/flags/so.svg | 11 + public/images/flags/sr.svg | 6 + public/images/flags/ss.svg | 8 + public/images/flags/st.svg | 16 + public/images/flags/sv.svg | 594 ++++++++++++ public/images/flags/sx.svg | 56 ++ public/images/flags/sy.svg | 6 + public/images/flags/sz.svg | 34 + public/images/flags/tc.svg | 50 ++ public/images/flags/td.svg | 7 + public/images/flags/tf.svg | 15 + public/images/flags/tg.svg | 14 + public/images/flags/th.svg | 7 + public/images/flags/tj.svg | 22 + public/images/flags/tk.svg | 5 + public/images/flags/tl.svg | 13 + public/images/flags/tm.svg | 204 +++++ public/images/flags/tn.svg | 4 + public/images/flags/to.svg | 10 + public/images/flags/tr.svg | 8 + public/images/flags/tt.svg | 5 + public/images/flags/tv.svg | 9 + public/images/flags/tw.svg | 34 + public/images/flags/tz.svg | 13 + public/images/flags/ua.svg | 6 + public/images/flags/ug.svg | 30 + public/images/flags/um.svg | 9 + public/images/flags/un.svg | 16 + public/images/flags/us.svg | 9 + public/images/flags/uy.svg | 28 + public/images/flags/uz.svg | 30 + public/images/flags/va.svg | 190 ++++ public/images/flags/vc.svg | 8 + public/images/flags/ve.svg | 26 + public/images/flags/vg.svg | 59 ++ public/images/flags/vi.svg | 28 + public/images/flags/vn.svg | 11 + public/images/flags/vu.svg | 21 + public/images/flags/wf.svg | 5 + public/images/flags/ws.svg | 7 + public/images/flags/xk.svg | 5 + public/images/flags/xx.svg | 4 + public/images/flags/ye.svg | 7 + public/images/flags/yt.svg | 5 + public/images/flags/za.svg | 17 + public/images/flags/zm.svg | 27 + public/images/flags/zw.svg | 21 + .../src/app/components/Editor/Field/Field.tsx | 5 +- .../ItemEditHeader/LanguageSelector.tsx | 17 +- .../src/app/views/ItemList/ItemListTable.tsx | 38 +- .../components/AddFieldModal/DefaultValue.tsx | 3 + .../AddFieldModal/DefaultValueInput.tsx | 9 +- .../AddFieldModal/FieldFormInput.tsx | 69 +- .../AddFieldModal/views/FieldForm.tsx | 96 ++ .../components/AddFieldModal/views/Rules.tsx | 1 + src/apps/schema/src/app/components/configs.ts | 19 +- .../FieldTypeCurrency/FieldTypeCurrency.js | 69 -- .../FieldTypeCurrency/FieldTypeCurrency.less | 52 -- .../{currencies.js => currencies.ts} | 845 ++++++++++-------- .../components/FieldTypeCurrency/index.js | 1 - .../components/FieldTypeCurrency/index.tsx | 64 ++ src/shell/components/FieldTypeNumber.tsx | 6 +- .../components/NumberFormatInput/index.tsx | 11 +- src/shell/services/types.ts | 1 + src/utility/getFlagEmoji.ts | 9 + 290 files changed, 11582 insertions(+), 535 deletions(-) create mode 100644 public/images/flags/ad.svg create mode 100644 public/images/flags/ae.svg create mode 100644 public/images/flags/af.svg create mode 100644 public/images/flags/ag.svg create mode 100644 public/images/flags/ai.svg create mode 100644 public/images/flags/al.svg create mode 100644 public/images/flags/am.svg create mode 100644 public/images/flags/ao.svg create mode 100644 public/images/flags/aq.svg create mode 100644 public/images/flags/ar.svg create mode 100644 public/images/flags/arab.svg create mode 100644 public/images/flags/as.svg create mode 100644 public/images/flags/at.svg create mode 100644 public/images/flags/au.svg create mode 100644 public/images/flags/aw.svg create mode 100644 public/images/flags/ax.svg create mode 100644 public/images/flags/az.svg create mode 100644 public/images/flags/ba.svg create mode 100644 public/images/flags/bb.svg create mode 100644 public/images/flags/bd.svg create mode 100644 public/images/flags/be.svg create mode 100644 public/images/flags/bf.svg create mode 100644 public/images/flags/bg.svg create mode 100644 public/images/flags/bh.svg create mode 100644 public/images/flags/bi.svg create mode 100644 public/images/flags/bj.svg create mode 100644 public/images/flags/bl.svg create mode 100644 public/images/flags/bm.svg create mode 100644 public/images/flags/bn.svg create mode 100644 public/images/flags/bo.svg create mode 100644 public/images/flags/bq.svg create mode 100644 public/images/flags/br.svg create mode 100644 public/images/flags/bs.svg create mode 100644 public/images/flags/bt.svg create mode 100644 public/images/flags/bv.svg create mode 100644 public/images/flags/bw.svg create mode 100644 public/images/flags/by.svg create mode 100644 public/images/flags/bz.svg create mode 100644 public/images/flags/ca.svg create mode 100644 public/images/flags/cc.svg create mode 100644 public/images/flags/cd.svg create mode 100644 public/images/flags/cefta.svg create mode 100644 public/images/flags/cf.svg create mode 100644 public/images/flags/cg.svg create mode 100644 public/images/flags/ch.svg create mode 100644 public/images/flags/ci.svg create mode 100644 public/images/flags/ck.svg create mode 100644 public/images/flags/cl.svg create mode 100644 public/images/flags/cm.svg create mode 100644 public/images/flags/cn.svg create mode 100644 public/images/flags/co.svg create mode 100644 public/images/flags/cp.svg create mode 100644 public/images/flags/cr.svg create mode 100644 public/images/flags/cu.svg create mode 100644 public/images/flags/cv.svg create mode 100644 public/images/flags/cw.svg create mode 100644 public/images/flags/cx.svg create mode 100644 public/images/flags/cy.svg create mode 100644 public/images/flags/cz.svg create mode 100644 public/images/flags/de.svg create mode 100644 public/images/flags/dg.svg create mode 100644 public/images/flags/dj.svg create mode 100644 public/images/flags/dk.svg create mode 100644 public/images/flags/dm.svg create mode 100644 public/images/flags/do.svg create mode 100644 public/images/flags/dz.svg create mode 100644 public/images/flags/eac.svg create mode 100644 public/images/flags/ec.svg create mode 100644 public/images/flags/ee.svg create mode 100644 public/images/flags/eg.svg create mode 100644 public/images/flags/eh.svg create mode 100644 public/images/flags/er.svg create mode 100644 public/images/flags/es-ct.svg create mode 100644 public/images/flags/es-ga.svg create mode 100644 public/images/flags/es-pv.svg create mode 100644 public/images/flags/es.svg create mode 100644 public/images/flags/et.svg create mode 100644 public/images/flags/eu.svg create mode 100644 public/images/flags/fi.svg create mode 100644 public/images/flags/fj.svg create mode 100644 public/images/flags/fk.svg create mode 100644 public/images/flags/fm.svg create mode 100644 public/images/flags/fo.svg create mode 100644 public/images/flags/fr.svg create mode 100644 public/images/flags/ga.svg create mode 100644 public/images/flags/gb-eng.svg create mode 100644 public/images/flags/gb-nir.svg create mode 100644 public/images/flags/gb-sct.svg create mode 100644 public/images/flags/gb-wls.svg create mode 100644 public/images/flags/gb.svg create mode 100644 public/images/flags/gd.svg create mode 100644 public/images/flags/ge.svg create mode 100644 public/images/flags/gf.svg create mode 100644 public/images/flags/gg.svg create mode 100644 public/images/flags/gh.svg create mode 100644 public/images/flags/gi.svg create mode 100644 public/images/flags/gl.svg create mode 100644 public/images/flags/gm.svg create mode 100644 public/images/flags/gn.svg create mode 100644 public/images/flags/gp.svg create mode 100644 public/images/flags/gq.svg create mode 100644 public/images/flags/gr.svg create mode 100644 public/images/flags/gs.svg create mode 100644 public/images/flags/gt.svg create mode 100644 public/images/flags/gu.svg create mode 100644 public/images/flags/gw.svg create mode 100644 public/images/flags/gy.svg create mode 100644 public/images/flags/hk.svg create mode 100644 public/images/flags/hm.svg create mode 100644 public/images/flags/hn.svg create mode 100644 public/images/flags/hr.svg create mode 100644 public/images/flags/ht.svg create mode 100644 public/images/flags/hu.svg create mode 100644 public/images/flags/ic.svg create mode 100644 public/images/flags/id.svg create mode 100644 public/images/flags/ie.svg create mode 100644 public/images/flags/il.svg create mode 100644 public/images/flags/im.svg create mode 100644 public/images/flags/in.svg create mode 100644 public/images/flags/io.svg create mode 100644 public/images/flags/iq.svg create mode 100644 public/images/flags/ir.svg create mode 100644 public/images/flags/is.svg create mode 100644 public/images/flags/it.svg create mode 100644 public/images/flags/je.svg create mode 100644 public/images/flags/jm.svg create mode 100644 public/images/flags/jo.svg create mode 100644 public/images/flags/jp.svg create mode 100644 public/images/flags/ke.svg create mode 100644 public/images/flags/kg.svg create mode 100644 public/images/flags/kh.svg create mode 100644 public/images/flags/ki.svg create mode 100644 public/images/flags/km.svg create mode 100644 public/images/flags/kn.svg create mode 100644 public/images/flags/kp.svg create mode 100644 public/images/flags/kr.svg create mode 100644 public/images/flags/kw.svg create mode 100644 public/images/flags/ky.svg create mode 100644 public/images/flags/kz.svg create mode 100644 public/images/flags/la.svg create mode 100644 public/images/flags/lb.svg create mode 100644 public/images/flags/lc.svg create mode 100644 public/images/flags/li.svg create mode 100644 public/images/flags/lk.svg create mode 100644 public/images/flags/lr.svg create mode 100644 public/images/flags/ls.svg create mode 100644 public/images/flags/lt.svg create mode 100644 public/images/flags/lu.svg create mode 100644 public/images/flags/lv.svg create mode 100644 public/images/flags/ly.svg create mode 100644 public/images/flags/ma.svg create mode 100644 public/images/flags/mc.svg create mode 100644 public/images/flags/md.svg create mode 100644 public/images/flags/me.svg create mode 100644 public/images/flags/mf.svg create mode 100644 public/images/flags/mg.svg create mode 100644 public/images/flags/mh.svg create mode 100644 public/images/flags/mk.svg create mode 100644 public/images/flags/ml.svg create mode 100644 public/images/flags/mm.svg create mode 100644 public/images/flags/mn.svg create mode 100644 public/images/flags/mo.svg create mode 100644 public/images/flags/mp.svg create mode 100644 public/images/flags/mq.svg create mode 100644 public/images/flags/mr.svg create mode 100644 public/images/flags/ms.svg create mode 100644 public/images/flags/mt.svg create mode 100644 public/images/flags/mu.svg create mode 100644 public/images/flags/mv.svg create mode 100644 public/images/flags/mw.svg create mode 100644 public/images/flags/mx.svg create mode 100644 public/images/flags/my.svg create mode 100644 public/images/flags/mz.svg create mode 100644 public/images/flags/na.svg create mode 100644 public/images/flags/nc.svg create mode 100644 public/images/flags/ne.svg create mode 100644 public/images/flags/nf.svg create mode 100644 public/images/flags/ng.svg create mode 100644 public/images/flags/ni.svg create mode 100644 public/images/flags/nl.svg create mode 100644 public/images/flags/no.svg create mode 100644 public/images/flags/np.svg create mode 100644 public/images/flags/nr.svg create mode 100644 public/images/flags/nu.svg create mode 100644 public/images/flags/nz.svg create mode 100644 public/images/flags/om.svg create mode 100644 public/images/flags/pa.svg create mode 100644 public/images/flags/pc.svg create mode 100644 public/images/flags/pe.svg create mode 100644 public/images/flags/pf.svg create mode 100644 public/images/flags/pg.svg create mode 100644 public/images/flags/ph.svg create mode 100644 public/images/flags/pk.svg create mode 100644 public/images/flags/pl.svg create mode 100644 public/images/flags/pm.svg create mode 100644 public/images/flags/pn.svg create mode 100644 public/images/flags/pr.svg create mode 100644 public/images/flags/ps.svg create mode 100644 public/images/flags/pt.svg create mode 100644 public/images/flags/pw.svg create mode 100644 public/images/flags/py.svg create mode 100644 public/images/flags/qa.svg create mode 100644 public/images/flags/re.svg create mode 100644 public/images/flags/ro.svg create mode 100644 public/images/flags/rs.svg create mode 100644 public/images/flags/ru.svg create mode 100644 public/images/flags/rw.svg create mode 100644 public/images/flags/sa.svg create mode 100644 public/images/flags/sb.svg create mode 100644 public/images/flags/sc.svg create mode 100644 public/images/flags/sd.svg create mode 100644 public/images/flags/se.svg create mode 100644 public/images/flags/sg.svg create mode 100644 public/images/flags/sh-ac.svg create mode 100644 public/images/flags/sh-hl.svg create mode 100644 public/images/flags/sh-ta.svg create mode 100644 public/images/flags/sh.svg create mode 100644 public/images/flags/si.svg create mode 100644 public/images/flags/sj.svg create mode 100644 public/images/flags/sk.svg create mode 100644 public/images/flags/sl.svg create mode 100644 public/images/flags/sm.svg create mode 100644 public/images/flags/sn.svg create mode 100644 public/images/flags/so.svg create mode 100644 public/images/flags/sr.svg create mode 100644 public/images/flags/ss.svg create mode 100644 public/images/flags/st.svg create mode 100644 public/images/flags/sv.svg create mode 100644 public/images/flags/sx.svg create mode 100644 public/images/flags/sy.svg create mode 100644 public/images/flags/sz.svg create mode 100644 public/images/flags/tc.svg create mode 100644 public/images/flags/td.svg create mode 100644 public/images/flags/tf.svg create mode 100644 public/images/flags/tg.svg create mode 100644 public/images/flags/th.svg create mode 100644 public/images/flags/tj.svg create mode 100644 public/images/flags/tk.svg create mode 100644 public/images/flags/tl.svg create mode 100644 public/images/flags/tm.svg create mode 100644 public/images/flags/tn.svg create mode 100644 public/images/flags/to.svg create mode 100644 public/images/flags/tr.svg create mode 100644 public/images/flags/tt.svg create mode 100644 public/images/flags/tv.svg create mode 100644 public/images/flags/tw.svg create mode 100644 public/images/flags/tz.svg create mode 100644 public/images/flags/ua.svg create mode 100644 public/images/flags/ug.svg create mode 100644 public/images/flags/um.svg create mode 100644 public/images/flags/un.svg create mode 100644 public/images/flags/us.svg create mode 100644 public/images/flags/uy.svg create mode 100644 public/images/flags/uz.svg create mode 100644 public/images/flags/va.svg create mode 100644 public/images/flags/vc.svg create mode 100644 public/images/flags/ve.svg create mode 100644 public/images/flags/vg.svg create mode 100644 public/images/flags/vi.svg create mode 100644 public/images/flags/vn.svg create mode 100644 public/images/flags/vu.svg create mode 100644 public/images/flags/wf.svg create mode 100644 public/images/flags/ws.svg create mode 100644 public/images/flags/xk.svg create mode 100644 public/images/flags/xx.svg create mode 100644 public/images/flags/ye.svg create mode 100644 public/images/flags/yt.svg create mode 100644 public/images/flags/za.svg create mode 100644 public/images/flags/zm.svg create mode 100644 public/images/flags/zw.svg delete mode 100644 src/shell/components/FieldTypeCurrency/FieldTypeCurrency.js delete mode 100644 src/shell/components/FieldTypeCurrency/FieldTypeCurrency.less rename src/shell/components/FieldTypeCurrency/{currencies.js => currencies.ts} (61%) delete mode 100644 src/shell/components/FieldTypeCurrency/index.js create mode 100644 src/shell/components/FieldTypeCurrency/index.tsx create mode 100644 src/utility/getFlagEmoji.ts diff --git a/cypress/e2e/content/content.spec.js b/cypress/e2e/content/content.spec.js index 0d629acca7..5762d0c1f6 100644 --- a/cypress/e2e/content/content.spec.js +++ b/cypress/e2e/content/content.spec.js @@ -184,7 +184,7 @@ describe("Content Specs", () => { }); it("Currency Field", () => { - cy.get("#12-b35c68-jd1s8s input[type=number]") + cy.get("#12-b35c68-jd1s8s input") .focus() .clear() .type("100.00") diff --git a/cypress/e2e/schema/field.spec.js b/cypress/e2e/schema/field.spec.js index 4ec52339ce..92625bf5b0 100644 --- a/cypress/e2e/schema/field.spec.js +++ b/cypress/e2e/schema/field.spec.js @@ -17,12 +17,14 @@ const SELECTORS = { FIELD_SELECT_MEDIA: "FieldItem_images", FIELD_SELECT_BOOLEAN: "FieldItem_yes_no", FIELD_SELECT_ONE_TO_ONE: "FieldItem_one_to_one", + FIELD_SELECT_CURRENCY: "FieldItem_currency", MEDIA_CHECKBOX_LIMIT: "MediaCheckbox_limit", MEDIA_CHECKBOX_LOCK: "MediaCheckbox_group_id", DROPDOWN_ADD_OPTION: "DropdownAddOption", DROPDOWN_DELETE_OPTION: "DeleteOption", AUTOCOMPLETE_MODEL_ZUID: "Autocomplete_relatedModelZUID", AUTOCOMPLETE_FIELED_ZUID: "Autocomplete_relatedFieldZUID", + AUTOCOMPLETE_FIELD_CURRENCY: "Autocomplete_currency", INPUT_LABEL: "FieldFormInput_label", INPUT_NAME: "FieldFormInput_name", INPUT_OPTION_LABEL: "OptionLabel", @@ -357,6 +359,44 @@ describe("Schema: Fields", () => { cy.getBySelector(`Field_${fieldName}`).should("exist"); }); + it("Creates a currency field", () => { + cy.intercept("**/fields?showDeleted=true").as("getFields"); + + const fieldLabel = `Currency ${timestamp}`; + const fieldName = `currency_${timestamp}`; + + // Open the add field modal + cy.getBySelector(SELECTORS.ADD_FIELD_BTN).should("exist").click(); + cy.getBySelector(SELECTORS.ADD_FIELD_MODAL).should("exist"); + + // Select one-to-one relationship field + cy.getBySelector(SELECTORS.FIELD_SELECT_CURRENCY).should("exist").click(); + + // Select default currency + cy.getBySelector(SELECTORS.AUTOCOMPLETE_FIELD_CURRENCY).type("phil"); + cy.get("[role=listbox] [role=option]").first().click(); + + // Fill up fields + cy.getBySelector(SELECTORS.INPUT_LABEL).should("exist").type(fieldLabel); + + // Navigate to rules tab and add default value + cy.getBySelector(SELECTORS.RULES_TAB_BTN).click(); + // click on the default value checkbox + cy.getBySelector(SELECTORS.DEFAULT_VALUE_CHECKBOX).click(); + // enter a default value + cy.getBySelector(SELECTORS.DEFAULT_VALUE_INPUT).type("1000.50"); + // Verify default currency + cy.getBySelector(SELECTORS.DEFAULT_VALUE_INPUT).contains("PHP"); + // Click done + cy.getBySelector(SELECTORS.SAVE_FIELD_BUTTON).should("exist").click(); + cy.getBySelector(SELECTORS.ADD_FIELD_MODAL).should("not.exist"); + + cy.wait("@getFields"); + + // Check if field exists + cy.getBySelector(`Field_${fieldName}`).should("exist"); + }); + it("Creates a field via add another field button", () => { cy.intercept("**/fields?showDeleted=true").as("getFields"); diff --git a/public/images/flags/ad.svg b/public/images/flags/ad.svg new file mode 100644 index 0000000000..067ab772f6 --- /dev/null +++ b/public/images/flags/ad.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ae.svg b/public/images/flags/ae.svg new file mode 100644 index 0000000000..651ac8523d --- /dev/null +++ b/public/images/flags/ae.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/af.svg b/public/images/flags/af.svg new file mode 100644 index 0000000000..521ac4cfd8 --- /dev/null +++ b/public/images/flags/af.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ag.svg b/public/images/flags/ag.svg new file mode 100644 index 0000000000..243c3d8f9e --- /dev/null +++ b/public/images/flags/ag.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/ai.svg b/public/images/flags/ai.svg new file mode 100644 index 0000000000..628ad9be93 --- /dev/null +++ b/public/images/flags/ai.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/al.svg b/public/images/flags/al.svg new file mode 100644 index 0000000000..1135b4b80a --- /dev/null +++ b/public/images/flags/al.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/am.svg b/public/images/flags/am.svg new file mode 100644 index 0000000000..99fa4dc597 --- /dev/null +++ b/public/images/flags/am.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ao.svg b/public/images/flags/ao.svg new file mode 100644 index 0000000000..b1863bd0f6 --- /dev/null +++ b/public/images/flags/ao.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/aq.svg b/public/images/flags/aq.svg new file mode 100644 index 0000000000..53840cccb0 --- /dev/null +++ b/public/images/flags/aq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ar.svg b/public/images/flags/ar.svg new file mode 100644 index 0000000000..d20cbbdcdc --- /dev/null +++ b/public/images/flags/ar.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/arab.svg b/public/images/flags/arab.svg new file mode 100644 index 0000000000..96d27157e9 --- /dev/null +++ b/public/images/flags/arab.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/as.svg b/public/images/flags/as.svg new file mode 100644 index 0000000000..3543556725 --- /dev/null +++ b/public/images/flags/as.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/at.svg b/public/images/flags/at.svg new file mode 100644 index 0000000000..9d2775c083 --- /dev/null +++ b/public/images/flags/at.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/au.svg b/public/images/flags/au.svg new file mode 100644 index 0000000000..96e80768bb --- /dev/null +++ b/public/images/flags/au.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/aw.svg b/public/images/flags/aw.svg new file mode 100644 index 0000000000..413b7c45b6 --- /dev/null +++ b/public/images/flags/aw.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ax.svg b/public/images/flags/ax.svg new file mode 100644 index 0000000000..0584d713b5 --- /dev/null +++ b/public/images/flags/ax.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/az.svg b/public/images/flags/az.svg new file mode 100644 index 0000000000..3557522110 --- /dev/null +++ b/public/images/flags/az.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/ba.svg b/public/images/flags/ba.svg new file mode 100644 index 0000000000..93bd9cf937 --- /dev/null +++ b/public/images/flags/ba.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/bb.svg b/public/images/flags/bb.svg new file mode 100644 index 0000000000..cecd5cc334 --- /dev/null +++ b/public/images/flags/bb.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/bd.svg b/public/images/flags/bd.svg new file mode 100644 index 0000000000..16b794debd --- /dev/null +++ b/public/images/flags/bd.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/be.svg b/public/images/flags/be.svg new file mode 100644 index 0000000000..ac706a0b5a --- /dev/null +++ b/public/images/flags/be.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/bf.svg b/public/images/flags/bf.svg new file mode 100644 index 0000000000..4713822584 --- /dev/null +++ b/public/images/flags/bf.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/bg.svg b/public/images/flags/bg.svg new file mode 100644 index 0000000000..af2d0d07c3 --- /dev/null +++ b/public/images/flags/bg.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/bh.svg b/public/images/flags/bh.svg new file mode 100644 index 0000000000..7a2ea549b6 --- /dev/null +++ b/public/images/flags/bh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/bi.svg b/public/images/flags/bi.svg new file mode 100644 index 0000000000..a4434a955f --- /dev/null +++ b/public/images/flags/bi.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/bj.svg b/public/images/flags/bj.svg new file mode 100644 index 0000000000..0846724d17 --- /dev/null +++ b/public/images/flags/bj.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/bl.svg b/public/images/flags/bl.svg new file mode 100644 index 0000000000..f84cbbaeb1 --- /dev/null +++ b/public/images/flags/bl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/bm.svg b/public/images/flags/bm.svg new file mode 100644 index 0000000000..bab3e0abe0 --- /dev/null +++ b/public/images/flags/bm.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bn.svg b/public/images/flags/bn.svg new file mode 100644 index 0000000000..4b416ebb73 --- /dev/null +++ b/public/images/flags/bn.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bo.svg b/public/images/flags/bo.svg new file mode 100644 index 0000000000..46dc76735e --- /dev/null +++ b/public/images/flags/bo.svg @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bq.svg b/public/images/flags/bq.svg new file mode 100644 index 0000000000..0e6bc76e62 --- /dev/null +++ b/public/images/flags/bq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/br.svg b/public/images/flags/br.svg new file mode 100644 index 0000000000..22c908e7e3 --- /dev/null +++ b/public/images/flags/br.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bs.svg b/public/images/flags/bs.svg new file mode 100644 index 0000000000..5cc918e5ad --- /dev/null +++ b/public/images/flags/bs.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/bt.svg b/public/images/flags/bt.svg new file mode 100644 index 0000000000..798c79b381 --- /dev/null +++ b/public/images/flags/bt.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bv.svg b/public/images/flags/bv.svg new file mode 100644 index 0000000000..40e16d9482 --- /dev/null +++ b/public/images/flags/bv.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/bw.svg b/public/images/flags/bw.svg new file mode 100644 index 0000000000..3435608d6c --- /dev/null +++ b/public/images/flags/bw.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/by.svg b/public/images/flags/by.svg new file mode 100644 index 0000000000..7e90ff255c --- /dev/null +++ b/public/images/flags/by.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/bz.svg b/public/images/flags/bz.svg new file mode 100644 index 0000000000..25386a51a4 --- /dev/null +++ b/public/images/flags/bz.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ca.svg b/public/images/flags/ca.svg new file mode 100644 index 0000000000..89da5b7b55 --- /dev/null +++ b/public/images/flags/ca.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/cc.svg b/public/images/flags/cc.svg new file mode 100644 index 0000000000..ddfd180382 --- /dev/null +++ b/public/images/flags/cc.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/cd.svg b/public/images/flags/cd.svg new file mode 100644 index 0000000000..b9cf528941 --- /dev/null +++ b/public/images/flags/cd.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/cefta.svg b/public/images/flags/cefta.svg new file mode 100644 index 0000000000..f748d08a12 --- /dev/null +++ b/public/images/flags/cefta.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/cf.svg b/public/images/flags/cf.svg new file mode 100644 index 0000000000..a6cd3670f2 --- /dev/null +++ b/public/images/flags/cf.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/cg.svg b/public/images/flags/cg.svg new file mode 100644 index 0000000000..f5a0e42d45 --- /dev/null +++ b/public/images/flags/cg.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/ch.svg b/public/images/flags/ch.svg new file mode 100644 index 0000000000..b42d6709cf --- /dev/null +++ b/public/images/flags/ch.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/ci.svg b/public/images/flags/ci.svg new file mode 100644 index 0000000000..e400f0c1cd --- /dev/null +++ b/public/images/flags/ci.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/ck.svg b/public/images/flags/ck.svg new file mode 100644 index 0000000000..18e547b17d --- /dev/null +++ b/public/images/flags/ck.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/cl.svg b/public/images/flags/cl.svg new file mode 100644 index 0000000000..5b3c72fa7c --- /dev/null +++ b/public/images/flags/cl.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/cm.svg b/public/images/flags/cm.svg new file mode 100644 index 0000000000..70adc8b681 --- /dev/null +++ b/public/images/flags/cm.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/cn.svg b/public/images/flags/cn.svg new file mode 100644 index 0000000000..10d3489a0e --- /dev/null +++ b/public/images/flags/cn.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/co.svg b/public/images/flags/co.svg new file mode 100644 index 0000000000..ebd0a0fb2d --- /dev/null +++ b/public/images/flags/co.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/cp.svg b/public/images/flags/cp.svg new file mode 100644 index 0000000000..b8aa9cfd69 --- /dev/null +++ b/public/images/flags/cp.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/cr.svg b/public/images/flags/cr.svg new file mode 100644 index 0000000000..5a409eebb2 --- /dev/null +++ b/public/images/flags/cr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/cu.svg b/public/images/flags/cu.svg new file mode 100644 index 0000000000..053c9ee3a0 --- /dev/null +++ b/public/images/flags/cu.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/cv.svg b/public/images/flags/cv.svg new file mode 100644 index 0000000000..aec8994902 --- /dev/null +++ b/public/images/flags/cv.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/cw.svg b/public/images/flags/cw.svg new file mode 100644 index 0000000000..bb0ece22e4 --- /dev/null +++ b/public/images/flags/cw.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/cx.svg b/public/images/flags/cx.svg new file mode 100644 index 0000000000..374ff2dab5 --- /dev/null +++ b/public/images/flags/cx.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/cy.svg b/public/images/flags/cy.svg new file mode 100644 index 0000000000..7e3d883da8 --- /dev/null +++ b/public/images/flags/cy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/cz.svg b/public/images/flags/cz.svg new file mode 100644 index 0000000000..7913de3895 --- /dev/null +++ b/public/images/flags/cz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/de.svg b/public/images/flags/de.svg new file mode 100644 index 0000000000..71aa2d2c30 --- /dev/null +++ b/public/images/flags/de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/dg.svg b/public/images/flags/dg.svg new file mode 100644 index 0000000000..f163caf947 --- /dev/null +++ b/public/images/flags/dg.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/dj.svg b/public/images/flags/dj.svg new file mode 100644 index 0000000000..9b00a82056 --- /dev/null +++ b/public/images/flags/dj.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/dk.svg b/public/images/flags/dk.svg new file mode 100644 index 0000000000..563277f81d --- /dev/null +++ b/public/images/flags/dk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/dm.svg b/public/images/flags/dm.svg new file mode 100644 index 0000000000..f692094ddb --- /dev/null +++ b/public/images/flags/dm.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/do.svg b/public/images/flags/do.svg new file mode 100644 index 0000000000..b1be393ed1 --- /dev/null +++ b/public/images/flags/do.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/dz.svg b/public/images/flags/dz.svg new file mode 100644 index 0000000000..5ff29a74a0 --- /dev/null +++ b/public/images/flags/dz.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/eac.svg b/public/images/flags/eac.svg new file mode 100644 index 0000000000..aaf8133f35 --- /dev/null +++ b/public/images/flags/eac.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ec.svg b/public/images/flags/ec.svg new file mode 100644 index 0000000000..397bfd9822 --- /dev/null +++ b/public/images/flags/ec.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ee.svg b/public/images/flags/ee.svg new file mode 100644 index 0000000000..8b98c2c429 --- /dev/null +++ b/public/images/flags/ee.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/eg.svg b/public/images/flags/eg.svg new file mode 100644 index 0000000000..00d1fa59ee --- /dev/null +++ b/public/images/flags/eg.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/eh.svg b/public/images/flags/eh.svg new file mode 100644 index 0000000000..6aec72883c --- /dev/null +++ b/public/images/flags/eh.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/er.svg b/public/images/flags/er.svg new file mode 100644 index 0000000000..3f4f3f2921 --- /dev/null +++ b/public/images/flags/er.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/es-ct.svg b/public/images/flags/es-ct.svg new file mode 100644 index 0000000000..4d85911402 --- /dev/null +++ b/public/images/flags/es-ct.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/es-ga.svg b/public/images/flags/es-ga.svg new file mode 100644 index 0000000000..31657813ea --- /dev/null +++ b/public/images/flags/es-ga.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/es-pv.svg b/public/images/flags/es-pv.svg new file mode 100644 index 0000000000..21c8759ec0 --- /dev/null +++ b/public/images/flags/es-pv.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/es.svg b/public/images/flags/es.svg new file mode 100644 index 0000000000..acdf927f23 --- /dev/null +++ b/public/images/flags/es.svg @@ -0,0 +1,544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/et.svg b/public/images/flags/et.svg new file mode 100644 index 0000000000..3f99be4860 --- /dev/null +++ b/public/images/flags/et.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/eu.svg b/public/images/flags/eu.svg new file mode 100644 index 0000000000..b0874c1ed4 --- /dev/null +++ b/public/images/flags/eu.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/fi.svg b/public/images/flags/fi.svg new file mode 100644 index 0000000000..470be2d07c --- /dev/null +++ b/public/images/flags/fi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/fj.svg b/public/images/flags/fj.svg new file mode 100644 index 0000000000..23fbe57a8d --- /dev/null +++ b/public/images/flags/fj.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/fk.svg b/public/images/flags/fk.svg new file mode 100644 index 0000000000..c65bf96de9 --- /dev/null +++ b/public/images/flags/fk.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/fm.svg b/public/images/flags/fm.svg new file mode 100644 index 0000000000..c1b7c97784 --- /dev/null +++ b/public/images/flags/fm.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/fo.svg b/public/images/flags/fo.svg new file mode 100644 index 0000000000..f802d285ac --- /dev/null +++ b/public/images/flags/fo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/fr.svg b/public/images/flags/fr.svg new file mode 100644 index 0000000000..4110e59e4c --- /dev/null +++ b/public/images/flags/fr.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ga.svg b/public/images/flags/ga.svg new file mode 100644 index 0000000000..76edab429c --- /dev/null +++ b/public/images/flags/ga.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/gb-eng.svg b/public/images/flags/gb-eng.svg new file mode 100644 index 0000000000..12e3b67d56 --- /dev/null +++ b/public/images/flags/gb-eng.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/gb-nir.svg b/public/images/flags/gb-nir.svg new file mode 100644 index 0000000000..e6be8dbc2d --- /dev/null +++ b/public/images/flags/gb-nir.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gb-sct.svg b/public/images/flags/gb-sct.svg new file mode 100644 index 0000000000..f50cd322ac --- /dev/null +++ b/public/images/flags/gb-sct.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/gb-wls.svg b/public/images/flags/gb-wls.svg new file mode 100644 index 0000000000..6e15fd0158 --- /dev/null +++ b/public/images/flags/gb-wls.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/gb.svg b/public/images/flags/gb.svg new file mode 100644 index 0000000000..799138319d --- /dev/null +++ b/public/images/flags/gb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/gd.svg b/public/images/flags/gd.svg new file mode 100644 index 0000000000..cb51e9618e --- /dev/null +++ b/public/images/flags/gd.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ge.svg b/public/images/flags/ge.svg new file mode 100644 index 0000000000..d8126ec8d8 --- /dev/null +++ b/public/images/flags/ge.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/gf.svg b/public/images/flags/gf.svg new file mode 100644 index 0000000000..f8fe94c659 --- /dev/null +++ b/public/images/flags/gf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/gg.svg b/public/images/flags/gg.svg new file mode 100644 index 0000000000..f8216c8bc1 --- /dev/null +++ b/public/images/flags/gg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/gh.svg b/public/images/flags/gh.svg new file mode 100644 index 0000000000..5c3e3e69ab --- /dev/null +++ b/public/images/flags/gh.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/gi.svg b/public/images/flags/gi.svg new file mode 100644 index 0000000000..e2b590afef --- /dev/null +++ b/public/images/flags/gi.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gl.svg b/public/images/flags/gl.svg new file mode 100644 index 0000000000..eb5a52e9e4 --- /dev/null +++ b/public/images/flags/gl.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/gm.svg b/public/images/flags/gm.svg new file mode 100644 index 0000000000..8fe9d66920 --- /dev/null +++ b/public/images/flags/gm.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/gn.svg b/public/images/flags/gn.svg new file mode 100644 index 0000000000..40d6ad4f03 --- /dev/null +++ b/public/images/flags/gn.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/gp.svg b/public/images/flags/gp.svg new file mode 100644 index 0000000000..ee55c4bcd3 --- /dev/null +++ b/public/images/flags/gp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/gq.svg b/public/images/flags/gq.svg new file mode 100644 index 0000000000..134e442173 --- /dev/null +++ b/public/images/flags/gq.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gr.svg b/public/images/flags/gr.svg new file mode 100644 index 0000000000..599741eec8 --- /dev/null +++ b/public/images/flags/gr.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gs.svg b/public/images/flags/gs.svg new file mode 100644 index 0000000000..1536e073ec --- /dev/null +++ b/public/images/flags/gs.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gt.svg b/public/images/flags/gt.svg new file mode 100644 index 0000000000..f7cffbdc7a --- /dev/null +++ b/public/images/flags/gt.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/gu.svg b/public/images/flags/gu.svg new file mode 100644 index 0000000000..0d66e1bfa8 --- /dev/null +++ b/public/images/flags/gu.svg @@ -0,0 +1,23 @@ + + + + + + + + + + G + U + A + M + + + + + + + + + + diff --git a/public/images/flags/gw.svg b/public/images/flags/gw.svg new file mode 100644 index 0000000000..d470bac9f7 --- /dev/null +++ b/public/images/flags/gw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/gy.svg b/public/images/flags/gy.svg new file mode 100644 index 0000000000..569fb56275 --- /dev/null +++ b/public/images/flags/gy.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/hk.svg b/public/images/flags/hk.svg new file mode 100644 index 0000000000..4fd55bc14b --- /dev/null +++ b/public/images/flags/hk.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/hm.svg b/public/images/flags/hm.svg new file mode 100644 index 0000000000..815c482085 --- /dev/null +++ b/public/images/flags/hm.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/hn.svg b/public/images/flags/hn.svg new file mode 100644 index 0000000000..11fde67db9 --- /dev/null +++ b/public/images/flags/hn.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/hr.svg b/public/images/flags/hr.svg new file mode 100644 index 0000000000..44fed27d54 --- /dev/null +++ b/public/images/flags/hr.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ht.svg b/public/images/flags/ht.svg new file mode 100644 index 0000000000..5d48eb93b2 --- /dev/null +++ b/public/images/flags/ht.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/hu.svg b/public/images/flags/hu.svg new file mode 100644 index 0000000000..baddf7f5ea --- /dev/null +++ b/public/images/flags/hu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/ic.svg b/public/images/flags/ic.svg new file mode 100644 index 0000000000..81e6ee2e13 --- /dev/null +++ b/public/images/flags/ic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/id.svg b/public/images/flags/id.svg new file mode 100644 index 0000000000..3b7c8fcfd9 --- /dev/null +++ b/public/images/flags/id.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/ie.svg b/public/images/flags/ie.svg new file mode 100644 index 0000000000..049be14de1 --- /dev/null +++ b/public/images/flags/ie.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/il.svg b/public/images/flags/il.svg new file mode 100644 index 0000000000..f43be7e8ed --- /dev/null +++ b/public/images/flags/il.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/im.svg b/public/images/flags/im.svg new file mode 100644 index 0000000000..f06f3d6fe1 --- /dev/null +++ b/public/images/flags/im.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/in.svg b/public/images/flags/in.svg new file mode 100644 index 0000000000..bc47d74911 --- /dev/null +++ b/public/images/flags/in.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/io.svg b/public/images/flags/io.svg new file mode 100644 index 0000000000..77016679ef --- /dev/null +++ b/public/images/flags/io.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/iq.svg b/public/images/flags/iq.svg new file mode 100644 index 0000000000..259da9adc5 --- /dev/null +++ b/public/images/flags/iq.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/flags/ir.svg b/public/images/flags/ir.svg new file mode 100644 index 0000000000..8c6d516216 --- /dev/null +++ b/public/images/flags/ir.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/is.svg b/public/images/flags/is.svg new file mode 100644 index 0000000000..a6588afaef --- /dev/null +++ b/public/images/flags/is.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/it.svg b/public/images/flags/it.svg new file mode 100644 index 0000000000..20a8bfdcc8 --- /dev/null +++ b/public/images/flags/it.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/je.svg b/public/images/flags/je.svg new file mode 100644 index 0000000000..611180d42a --- /dev/null +++ b/public/images/flags/je.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/jm.svg b/public/images/flags/jm.svg new file mode 100644 index 0000000000..269df03836 --- /dev/null +++ b/public/images/flags/jm.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/jo.svg b/public/images/flags/jo.svg new file mode 100644 index 0000000000..d6f927d44f --- /dev/null +++ b/public/images/flags/jo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/jp.svg b/public/images/flags/jp.svg new file mode 100644 index 0000000000..cc1c181ce9 --- /dev/null +++ b/public/images/flags/jp.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/ke.svg b/public/images/flags/ke.svg new file mode 100644 index 0000000000..3a67ca3ccd --- /dev/null +++ b/public/images/flags/ke.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/kg.svg b/public/images/flags/kg.svg new file mode 100644 index 0000000000..68c210b1cf --- /dev/null +++ b/public/images/flags/kg.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/kh.svg b/public/images/flags/kh.svg new file mode 100644 index 0000000000..c658838f4e --- /dev/null +++ b/public/images/flags/kh.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ki.svg b/public/images/flags/ki.svg new file mode 100644 index 0000000000..0c80328071 --- /dev/null +++ b/public/images/flags/ki.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/km.svg b/public/images/flags/km.svg new file mode 100644 index 0000000000..414d65e47f --- /dev/null +++ b/public/images/flags/km.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/kn.svg b/public/images/flags/kn.svg new file mode 100644 index 0000000000..47fe64d617 --- /dev/null +++ b/public/images/flags/kn.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/kp.svg b/public/images/flags/kp.svg new file mode 100644 index 0000000000..4d1dbab246 --- /dev/null +++ b/public/images/flags/kp.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/kr.svg b/public/images/flags/kr.svg new file mode 100644 index 0000000000..6947eab2b3 --- /dev/null +++ b/public/images/flags/kr.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/kw.svg b/public/images/flags/kw.svg new file mode 100644 index 0000000000..3dd89e9962 --- /dev/null +++ b/public/images/flags/kw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/ky.svg b/public/images/flags/ky.svg new file mode 100644 index 0000000000..74a2fea2a1 --- /dev/null +++ b/public/images/flags/ky.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/kz.svg b/public/images/flags/kz.svg new file mode 100644 index 0000000000..04a47f53e8 --- /dev/null +++ b/public/images/flags/kz.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/la.svg b/public/images/flags/la.svg new file mode 100644 index 0000000000..6aea6b72b4 --- /dev/null +++ b/public/images/flags/la.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/lb.svg b/public/images/flags/lb.svg new file mode 100644 index 0000000000..8619f2410e --- /dev/null +++ b/public/images/flags/lb.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/lc.svg b/public/images/flags/lc.svg new file mode 100644 index 0000000000..bb256541c6 --- /dev/null +++ b/public/images/flags/lc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/li.svg b/public/images/flags/li.svg new file mode 100644 index 0000000000..68ea26fa30 --- /dev/null +++ b/public/images/flags/li.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/lk.svg b/public/images/flags/lk.svg new file mode 100644 index 0000000000..2c5cdbe09d --- /dev/null +++ b/public/images/flags/lk.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/lr.svg b/public/images/flags/lr.svg new file mode 100644 index 0000000000..e482ab9d74 --- /dev/null +++ b/public/images/flags/lr.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/ls.svg b/public/images/flags/ls.svg new file mode 100644 index 0000000000..a7c01a98ff --- /dev/null +++ b/public/images/flags/ls.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/lt.svg b/public/images/flags/lt.svg new file mode 100644 index 0000000000..90ec5d240e --- /dev/null +++ b/public/images/flags/lt.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/lu.svg b/public/images/flags/lu.svg new file mode 100644 index 0000000000..cc12206812 --- /dev/null +++ b/public/images/flags/lu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/lv.svg b/public/images/flags/lv.svg new file mode 100644 index 0000000000..6a9e75ec97 --- /dev/null +++ b/public/images/flags/lv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/ly.svg b/public/images/flags/ly.svg new file mode 100644 index 0000000000..1eaa51e468 --- /dev/null +++ b/public/images/flags/ly.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/ma.svg b/public/images/flags/ma.svg new file mode 100644 index 0000000000..7ce56eff70 --- /dev/null +++ b/public/images/flags/ma.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/mc.svg b/public/images/flags/mc.svg new file mode 100644 index 0000000000..9cb6c9e8a0 --- /dev/null +++ b/public/images/flags/mc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/md.svg b/public/images/flags/md.svg new file mode 100644 index 0000000000..6dc441e177 --- /dev/null +++ b/public/images/flags/md.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/me.svg b/public/images/flags/me.svg new file mode 100644 index 0000000000..d891890746 --- /dev/null +++ b/public/images/flags/me.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/mf.svg b/public/images/flags/mf.svg new file mode 100644 index 0000000000..6305edc1c2 --- /dev/null +++ b/public/images/flags/mf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/mg.svg b/public/images/flags/mg.svg new file mode 100644 index 0000000000..5fa2d2440d --- /dev/null +++ b/public/images/flags/mg.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/mh.svg b/public/images/flags/mh.svg new file mode 100644 index 0000000000..7b9f490755 --- /dev/null +++ b/public/images/flags/mh.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/mk.svg b/public/images/flags/mk.svg new file mode 100644 index 0000000000..4f5cae77ed --- /dev/null +++ b/public/images/flags/mk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ml.svg b/public/images/flags/ml.svg new file mode 100644 index 0000000000..6f6b71695c --- /dev/null +++ b/public/images/flags/ml.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/mm.svg b/public/images/flags/mm.svg new file mode 100644 index 0000000000..42b4dee2b8 --- /dev/null +++ b/public/images/flags/mm.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/mn.svg b/public/images/flags/mn.svg new file mode 100644 index 0000000000..152c2fcb0f --- /dev/null +++ b/public/images/flags/mn.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/mo.svg b/public/images/flags/mo.svg new file mode 100644 index 0000000000..d39985d05f --- /dev/null +++ b/public/images/flags/mo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/mp.svg b/public/images/flags/mp.svg new file mode 100644 index 0000000000..ff59ebf87b --- /dev/null +++ b/public/images/flags/mp.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/mq.svg b/public/images/flags/mq.svg new file mode 100644 index 0000000000..b221951e36 --- /dev/null +++ b/public/images/flags/mq.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/mr.svg b/public/images/flags/mr.svg new file mode 100644 index 0000000000..7558234cbf --- /dev/null +++ b/public/images/flags/mr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/ms.svg b/public/images/flags/ms.svg new file mode 100644 index 0000000000..faf07b07fd --- /dev/null +++ b/public/images/flags/ms.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/mt.svg b/public/images/flags/mt.svg new file mode 100644 index 0000000000..c597266c36 --- /dev/null +++ b/public/images/flags/mt.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/mu.svg b/public/images/flags/mu.svg new file mode 100644 index 0000000000..82d7a3bec5 --- /dev/null +++ b/public/images/flags/mu.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/mv.svg b/public/images/flags/mv.svg new file mode 100644 index 0000000000..10450f9845 --- /dev/null +++ b/public/images/flags/mv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/mw.svg b/public/images/flags/mw.svg new file mode 100644 index 0000000000..d83ddb2178 --- /dev/null +++ b/public/images/flags/mw.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/flags/mx.svg b/public/images/flags/mx.svg new file mode 100644 index 0000000000..f98a89e173 --- /dev/null +++ b/public/images/flags/mx.svg @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/my.svg b/public/images/flags/my.svg new file mode 100644 index 0000000000..89576f69ea --- /dev/null +++ b/public/images/flags/my.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/mz.svg b/public/images/flags/mz.svg new file mode 100644 index 0000000000..2ee6ec14b4 --- /dev/null +++ b/public/images/flags/mz.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/na.svg b/public/images/flags/na.svg new file mode 100644 index 0000000000..35b9f783e1 --- /dev/null +++ b/public/images/flags/na.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/nc.svg b/public/images/flags/nc.svg new file mode 100644 index 0000000000..068f0c69aa --- /dev/null +++ b/public/images/flags/nc.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/ne.svg b/public/images/flags/ne.svg new file mode 100644 index 0000000000..39a82b8277 --- /dev/null +++ b/public/images/flags/ne.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/nf.svg b/public/images/flags/nf.svg new file mode 100644 index 0000000000..c8b30938d7 --- /dev/null +++ b/public/images/flags/nf.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/ng.svg b/public/images/flags/ng.svg new file mode 100644 index 0000000000..81eb35f78e --- /dev/null +++ b/public/images/flags/ng.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/ni.svg b/public/images/flags/ni.svg new file mode 100644 index 0000000000..6dcdc9a806 --- /dev/null +++ b/public/images/flags/ni.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/nl.svg b/public/images/flags/nl.svg new file mode 100644 index 0000000000..e90f5b0351 --- /dev/null +++ b/public/images/flags/nl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/no.svg b/public/images/flags/no.svg new file mode 100644 index 0000000000..a5f2a152a9 --- /dev/null +++ b/public/images/flags/no.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/np.svg b/public/images/flags/np.svg new file mode 100644 index 0000000000..8d71d106bb --- /dev/null +++ b/public/images/flags/np.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/nr.svg b/public/images/flags/nr.svg new file mode 100644 index 0000000000..ff394c4112 --- /dev/null +++ b/public/images/flags/nr.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/flags/nu.svg b/public/images/flags/nu.svg new file mode 100644 index 0000000000..4067bafff0 --- /dev/null +++ b/public/images/flags/nu.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/flags/nz.svg b/public/images/flags/nz.svg new file mode 100644 index 0000000000..935d8a749d --- /dev/null +++ b/public/images/flags/nz.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/om.svg b/public/images/flags/om.svg new file mode 100644 index 0000000000..c003f86e46 --- /dev/null +++ b/public/images/flags/om.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/pa.svg b/public/images/flags/pa.svg new file mode 100644 index 0000000000..8dc03bc61b --- /dev/null +++ b/public/images/flags/pa.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/pc.svg b/public/images/flags/pc.svg new file mode 100644 index 0000000000..882197da67 --- /dev/null +++ b/public/images/flags/pc.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/pe.svg b/public/images/flags/pe.svg new file mode 100644 index 0000000000..33e6cfd417 --- /dev/null +++ b/public/images/flags/pe.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/pf.svg b/public/images/flags/pf.svg new file mode 100644 index 0000000000..e06b236e82 --- /dev/null +++ b/public/images/flags/pf.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/pg.svg b/public/images/flags/pg.svg new file mode 100644 index 0000000000..237cb6eeed --- /dev/null +++ b/public/images/flags/pg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/ph.svg b/public/images/flags/ph.svg new file mode 100644 index 0000000000..65489e1cb2 --- /dev/null +++ b/public/images/flags/ph.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/pk.svg b/public/images/flags/pk.svg new file mode 100644 index 0000000000..491e58ab16 --- /dev/null +++ b/public/images/flags/pk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/pl.svg b/public/images/flags/pl.svg new file mode 100644 index 0000000000..0fa5145241 --- /dev/null +++ b/public/images/flags/pl.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/pm.svg b/public/images/flags/pm.svg new file mode 100644 index 0000000000..19a9330a31 --- /dev/null +++ b/public/images/flags/pm.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/pn.svg b/public/images/flags/pn.svg new file mode 100644 index 0000000000..07958aca12 --- /dev/null +++ b/public/images/flags/pn.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/pr.svg b/public/images/flags/pr.svg new file mode 100644 index 0000000000..ec51831dcd --- /dev/null +++ b/public/images/flags/pr.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/ps.svg b/public/images/flags/ps.svg new file mode 100644 index 0000000000..b33824a5dd --- /dev/null +++ b/public/images/flags/ps.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/pt.svg b/public/images/flags/pt.svg new file mode 100644 index 0000000000..445cf7f536 --- /dev/null +++ b/public/images/flags/pt.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/pw.svg b/public/images/flags/pw.svg new file mode 100644 index 0000000000..9f89c5f148 --- /dev/null +++ b/public/images/flags/pw.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/py.svg b/public/images/flags/py.svg new file mode 100644 index 0000000000..38e2051eb2 --- /dev/null +++ b/public/images/flags/py.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/qa.svg b/public/images/flags/qa.svg new file mode 100644 index 0000000000..901f3fa761 --- /dev/null +++ b/public/images/flags/qa.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/re.svg b/public/images/flags/re.svg new file mode 100644 index 0000000000..64e788e011 --- /dev/null +++ b/public/images/flags/re.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ro.svg b/public/images/flags/ro.svg new file mode 100644 index 0000000000..fda0f7bec9 --- /dev/null +++ b/public/images/flags/ro.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/rs.svg b/public/images/flags/rs.svg new file mode 100644 index 0000000000..2f971025b8 --- /dev/null +++ b/public/images/flags/rs.svg @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/ru.svg b/public/images/flags/ru.svg new file mode 100644 index 0000000000..cf243011ae --- /dev/null +++ b/public/images/flags/ru.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/rw.svg b/public/images/flags/rw.svg new file mode 100644 index 0000000000..06e26ae44e --- /dev/null +++ b/public/images/flags/rw.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/sa.svg b/public/images/flags/sa.svg new file mode 100644 index 0000000000..c0a148663b --- /dev/null +++ b/public/images/flags/sa.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sb.svg b/public/images/flags/sb.svg new file mode 100644 index 0000000000..6066f94cd1 --- /dev/null +++ b/public/images/flags/sb.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/sc.svg b/public/images/flags/sc.svg new file mode 100644 index 0000000000..9a46b369b3 --- /dev/null +++ b/public/images/flags/sc.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/sd.svg b/public/images/flags/sd.svg new file mode 100644 index 0000000000..12818b4110 --- /dev/null +++ b/public/images/flags/sd.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/se.svg b/public/images/flags/se.svg new file mode 100644 index 0000000000..8ba745acaf --- /dev/null +++ b/public/images/flags/se.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/sg.svg b/public/images/flags/sg.svg new file mode 100644 index 0000000000..c4dd4ac9eb --- /dev/null +++ b/public/images/flags/sg.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/sh-ac.svg b/public/images/flags/sh-ac.svg new file mode 100644 index 0000000000..22b365832e --- /dev/null +++ b/public/images/flags/sh-ac.svg @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sh-hl.svg b/public/images/flags/sh-hl.svg new file mode 100644 index 0000000000..b92e703f27 --- /dev/null +++ b/public/images/flags/sh-hl.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sh-ta.svg b/public/images/flags/sh-ta.svg new file mode 100644 index 0000000000..a103aac05f --- /dev/null +++ b/public/images/flags/sh-ta.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sh.svg b/public/images/flags/sh.svg new file mode 100644 index 0000000000..7aba0aec8a --- /dev/null +++ b/public/images/flags/sh.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/si.svg b/public/images/flags/si.svg new file mode 100644 index 0000000000..66a390dcd2 --- /dev/null +++ b/public/images/flags/si.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sj.svg b/public/images/flags/sj.svg new file mode 100644 index 0000000000..bb2799ce73 --- /dev/null +++ b/public/images/flags/sj.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/sk.svg b/public/images/flags/sk.svg new file mode 100644 index 0000000000..81476940eb --- /dev/null +++ b/public/images/flags/sk.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/sl.svg b/public/images/flags/sl.svg new file mode 100644 index 0000000000..a07baf75b4 --- /dev/null +++ b/public/images/flags/sl.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/sm.svg b/public/images/flags/sm.svg new file mode 100644 index 0000000000..00e9286c44 --- /dev/null +++ b/public/images/flags/sm.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sn.svg b/public/images/flags/sn.svg new file mode 100644 index 0000000000..7c0673d6d6 --- /dev/null +++ b/public/images/flags/sn.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/so.svg b/public/images/flags/so.svg new file mode 100644 index 0000000000..a581ac63cf --- /dev/null +++ b/public/images/flags/so.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/sr.svg b/public/images/flags/sr.svg new file mode 100644 index 0000000000..5e71c40026 --- /dev/null +++ b/public/images/flags/sr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/ss.svg b/public/images/flags/ss.svg new file mode 100644 index 0000000000..b257aa0b3e --- /dev/null +++ b/public/images/flags/ss.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/st.svg b/public/images/flags/st.svg new file mode 100644 index 0000000000..1294bcb70e --- /dev/null +++ b/public/images/flags/st.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sv.svg b/public/images/flags/sv.svg new file mode 100644 index 0000000000..c811e912f0 --- /dev/null +++ b/public/images/flags/sv.svg @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sx.svg b/public/images/flags/sx.svg new file mode 100644 index 0000000000..18f7a1397b --- /dev/null +++ b/public/images/flags/sx.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/sy.svg b/public/images/flags/sy.svg new file mode 100644 index 0000000000..5225550525 --- /dev/null +++ b/public/images/flags/sy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/sz.svg b/public/images/flags/sz.svg new file mode 100644 index 0000000000..294a2cc1a8 --- /dev/null +++ b/public/images/flags/sz.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/tc.svg b/public/images/flags/tc.svg new file mode 100644 index 0000000000..63f13c359b --- /dev/null +++ b/public/images/flags/tc.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/td.svg b/public/images/flags/td.svg new file mode 100644 index 0000000000..fa3bd927c1 --- /dev/null +++ b/public/images/flags/td.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/tf.svg b/public/images/flags/tf.svg new file mode 100644 index 0000000000..fba233563f --- /dev/null +++ b/public/images/flags/tf.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/flags/tg.svg b/public/images/flags/tg.svg new file mode 100644 index 0000000000..c63a6d1a94 --- /dev/null +++ b/public/images/flags/tg.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/flags/th.svg b/public/images/flags/th.svg new file mode 100644 index 0000000000..1e93a61e95 --- /dev/null +++ b/public/images/flags/th.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/tj.svg b/public/images/flags/tj.svg new file mode 100644 index 0000000000..9fba246cde --- /dev/null +++ b/public/images/flags/tj.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/tk.svg b/public/images/flags/tk.svg new file mode 100644 index 0000000000..05d3e86ce6 --- /dev/null +++ b/public/images/flags/tk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/tl.svg b/public/images/flags/tl.svg new file mode 100644 index 0000000000..3d0701a2c8 --- /dev/null +++ b/public/images/flags/tl.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/tm.svg b/public/images/flags/tm.svg new file mode 100644 index 0000000000..8b656cc2b8 --- /dev/null +++ b/public/images/flags/tm.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/tn.svg b/public/images/flags/tn.svg new file mode 100644 index 0000000000..5735c1984d --- /dev/null +++ b/public/images/flags/tn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/to.svg b/public/images/flags/to.svg new file mode 100644 index 0000000000..d072337066 --- /dev/null +++ b/public/images/flags/to.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/flags/tr.svg b/public/images/flags/tr.svg new file mode 100644 index 0000000000..b96da21f0e --- /dev/null +++ b/public/images/flags/tr.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/tt.svg b/public/images/flags/tt.svg new file mode 100644 index 0000000000..bc24938cf8 --- /dev/null +++ b/public/images/flags/tt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/tv.svg b/public/images/flags/tv.svg new file mode 100644 index 0000000000..675210ec55 --- /dev/null +++ b/public/images/flags/tv.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/tw.svg b/public/images/flags/tw.svg new file mode 100644 index 0000000000..57fd98b433 --- /dev/null +++ b/public/images/flags/tw.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/tz.svg b/public/images/flags/tz.svg new file mode 100644 index 0000000000..a2cfbca42a --- /dev/null +++ b/public/images/flags/tz.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/flags/ua.svg b/public/images/flags/ua.svg new file mode 100644 index 0000000000..a339eb1b9c --- /dev/null +++ b/public/images/flags/ua.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/flags/ug.svg b/public/images/flags/ug.svg new file mode 100644 index 0000000000..737eb2ce1a --- /dev/null +++ b/public/images/flags/ug.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/um.svg b/public/images/flags/um.svg new file mode 100644 index 0000000000..9e9eddaa4a --- /dev/null +++ b/public/images/flags/um.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/un.svg b/public/images/flags/un.svg new file mode 100644 index 0000000000..e57793bc79 --- /dev/null +++ b/public/images/flags/un.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/images/flags/us.svg b/public/images/flags/us.svg new file mode 100644 index 0000000000..9cfd0c927f --- /dev/null +++ b/public/images/flags/us.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/flags/uy.svg b/public/images/flags/uy.svg new file mode 100644 index 0000000000..62c36f8e5e --- /dev/null +++ b/public/images/flags/uy.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/uz.svg b/public/images/flags/uz.svg new file mode 100644 index 0000000000..0ccca1b1b4 --- /dev/null +++ b/public/images/flags/uz.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/va.svg b/public/images/flags/va.svg new file mode 100644 index 0000000000..87e0fbbdcc --- /dev/null +++ b/public/images/flags/va.svg @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/vc.svg b/public/images/flags/vc.svg new file mode 100644 index 0000000000..f26c2d8da9 --- /dev/null +++ b/public/images/flags/vc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/flags/ve.svg b/public/images/flags/ve.svg new file mode 100644 index 0000000000..314e7f5f7f --- /dev/null +++ b/public/images/flags/ve.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/vg.svg b/public/images/flags/vg.svg new file mode 100644 index 0000000000..0ee90fb28c --- /dev/null +++ b/public/images/flags/vg.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/vi.svg b/public/images/flags/vi.svg new file mode 100644 index 0000000000..4270257799 --- /dev/null +++ b/public/images/flags/vi.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/vn.svg b/public/images/flags/vn.svg new file mode 100644 index 0000000000..7e4bac8f4a --- /dev/null +++ b/public/images/flags/vn.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/flags/vu.svg b/public/images/flags/vu.svg new file mode 100644 index 0000000000..91e1236a0a --- /dev/null +++ b/public/images/flags/vu.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/wf.svg b/public/images/flags/wf.svg new file mode 100644 index 0000000000..054c57df99 --- /dev/null +++ b/public/images/flags/wf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/ws.svg b/public/images/flags/ws.svg new file mode 100644 index 0000000000..0e758a7a95 --- /dev/null +++ b/public/images/flags/ws.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/xk.svg b/public/images/flags/xk.svg new file mode 100644 index 0000000000..551e7a4145 --- /dev/null +++ b/public/images/flags/xk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/xx.svg b/public/images/flags/xx.svg new file mode 100644 index 0000000000..9333be3635 --- /dev/null +++ b/public/images/flags/xx.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/flags/ye.svg b/public/images/flags/ye.svg new file mode 100644 index 0000000000..1c9e6d6392 --- /dev/null +++ b/public/images/flags/ye.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/flags/yt.svg b/public/images/flags/yt.svg new file mode 100644 index 0000000000..e7776b3078 --- /dev/null +++ b/public/images/flags/yt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/flags/za.svg b/public/images/flags/za.svg new file mode 100644 index 0000000000..d563adb90c --- /dev/null +++ b/public/images/flags/za.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/zm.svg b/public/images/flags/zm.svg new file mode 100644 index 0000000000..13239f5e23 --- /dev/null +++ b/public/images/flags/zm.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/flags/zw.svg b/public/images/flags/zw.svg new file mode 100644 index 0000000000..6399ab4ab3 --- /dev/null +++ b/public/images/flags/zw.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx index d9a42aa90e..64c2e47f12 100644 --- a/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx +++ b/src/apps/content-editor/src/app/components/Editor/Field/Field.tsx @@ -882,10 +882,9 @@ export const Field = ({ errors={errors} > !!error)} /> diff --git a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/LanguageSelector.tsx b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/LanguageSelector.tsx index 617c83d054..8ed340b481 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/LanguageSelector.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/LanguageSelector.tsx @@ -11,6 +11,7 @@ import { useDispatch, useSelector } from "react-redux"; import { ContentItem } from "../../../../../../../../shell/services/types"; import { AppState } from "../../../../../../../../shell/store/types"; import { selectLang } from "../../../../../../../../shell/store/user"; +import getFlagEmoji from "../../../../../../../../utility/getFlagEmoji"; const getCountryCode = (langCode: string) => { const splitTag = langCode.split("-"); @@ -19,18 +20,6 @@ const getCountryCode = (langCode: string) => { return countryCode; }; -const getFlagEmojiFromIETFTag = (langCode: string) => { - const countryCode = getCountryCode(langCode); - - // Convert country code to flag emoji. - // Unicode flag emojis are made up of regional indicator symbols, which are a sequence of two letters. - const baseOffset = 0x1f1e6; - return ( - String.fromCodePoint(baseOffset + (countryCode.charCodeAt(0) - 65)) + - String.fromCodePoint(baseOffset + (countryCode.charCodeAt(1) - 65)) - ); -}; - export const LanguageSelector = () => { const dispatch = useDispatch(); const history = useHistory(); @@ -97,7 +86,7 @@ export const LanguageSelector = () => { data-cy="language-selector" > - {getFlagEmojiFromIETFTag(activeLanguage?.code)} + {getFlagEmoji(getCountryCode(activeLanguage?.code))} {" "} {activeLanguage?.code?.split("-")[0]?.toUpperCase()} ( {getCountryCode(activeLanguage?.code)}) @@ -130,7 +119,7 @@ export const LanguageSelector = () => { onSelect(language.code); }} > - {getFlagEmojiFromIETFTag(language.code)}{" "} + {getFlagEmoji(getCountryCode(language.code))}{" "} {language.code.split("-")[0]?.toUpperCase()} ( {getCountryCode(language.code)}) diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx index ca8ec799d0..273c3771a9 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx @@ -27,6 +27,8 @@ import { VersionCell } from "./TableCells/VersionCell"; import { DropDownCell } from "./TableCells/DropdownCell"; import { SortCell } from "./TableCells/SortCell"; import { BooleanCell } from "./TableCells/BooleanCell"; +import { currencies } from "../../../../../../shell/components/FieldTypeCurrency/currencies"; +import { Currency } from "../../../../../../shell/components/FieldTypeCurrency/currencies"; import { ImageCell } from "./TableCells/ImageCell"; import { SingleRelationshipCell } from "./TableCells/SingleRelationshipCell"; @@ -35,6 +37,18 @@ type ItemListTableProps = { rows: ContentItem[]; }; +const CURRENCY_OBJECT: Record = currencies.reduce( + (acc, curr) => { + return { + ...acc, + [curr.value]: { + ...curr, + }, + }; + }, + {} +); + const getHtmlText = (html: string) => { if (!html) return ""; @@ -136,11 +150,14 @@ const fieldTypeColumnConfigMap = { currency: { width: 160, valueFormatter: (params: any) => { - if (!params.value) return null; - return new Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", - }).format(params.value); + if (params.value?.value === undefined || params.value?.value === null) + return ""; + + return `${ + CURRENCY_OBJECT[params.value?.currency]?.symbol_native + } ${new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + }).format(params.value.value)}`; }, align: "right", }, @@ -293,7 +310,16 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { headerName: field.label, sortable: false, filterable: false, - valueGetter: (params: any) => params.row.data[field.name], + valueGetter: (params: any) => { + if (field.datatype === "currency") { + return { + value: params.row.data[field.name], + currency: field.settings?.currency || "USD", + }; + } + + return params.row.data[field.name]; + }, ...fieldTypeColumnConfigMap[field.datatype], // if field is yes_no but it has custom options increase the width ...(field.datatype === "yes_no" && diff --git a/src/apps/schema/src/app/components/AddFieldModal/DefaultValue.tsx b/src/apps/schema/src/app/components/AddFieldModal/DefaultValue.tsx index 4a57292a00..e9bd193e9c 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/DefaultValue.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/DefaultValue.tsx @@ -27,6 +27,7 @@ type DefaultValueProps = { relatedFieldZUID: string; }; options: FieldSettingsOptions[]; + currency?: string; }; export const DefaultValue = ({ @@ -39,6 +40,7 @@ export const DefaultValue = ({ mediaRules, relationshipFields, options, + currency, }: DefaultValueProps) => { return ( @@ -87,6 +89,7 @@ export const DefaultValue = ({ mediaRules={mediaRules} relationshipFields={relationshipFields} options={options} + currency={currency} /> diff --git a/src/apps/schema/src/app/components/AddFieldModal/DefaultValueInput.tsx b/src/apps/schema/src/app/components/AddFieldModal/DefaultValueInput.tsx index 633776d5a9..91c52679db 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/DefaultValueInput.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/DefaultValueInput.tsx @@ -65,6 +65,7 @@ type DefaultValueInputProps = { relatedFieldZUID: string; }; options: FieldSettingsOptions[]; + currency?: string; }; export const DefaultValueInput = ({ @@ -75,6 +76,7 @@ export const DefaultValueInput = ({ mediaRules, relationshipFields: { relatedModelZUID, relatedFieldZUID }, options, + currency, }: DefaultValueInputProps) => { const [imageModal, setImageModal] = useState(null); const dispatch = useDispatch(); @@ -573,12 +575,11 @@ export const DefaultValueInput = ({ return ( ); case "date": diff --git a/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx b/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx index 7e5eb773c1..500d57b262 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx @@ -14,6 +14,10 @@ import { Button, IconButton, Stack, + AutocompleteProps, + InputProps, + OutlinedInputProps, + FilledInputProps, } from "@mui/material"; import { SelectChangeEvent } from "@mui/material/Select"; import InfoRoundedIcon from "@mui/icons-material/InfoRounded"; @@ -25,6 +29,7 @@ import { FormValue } from "./views/FieldForm"; import { FieldSettingsOptions } from "../../../../../../shell/services/types"; import { convertDropdownValue } from "../../utils"; import { withCursorPosition } from "../../../../../../shell/components/withCursorPosition"; +import { Currency } from "../../../../../../shell/components/FieldTypeCurrency/currencies"; const TextFieldWithCursorPosition = withCursorPosition(TextField); @@ -50,6 +55,7 @@ export type FieldNames = | "regexRestrictErrorMessage" | "minValue" | "maxValue" + | "currency" | "fileExtensions" | "fileExtensionsErrorMessage"; type FieldType = @@ -80,7 +86,7 @@ export interface DropdownOptions { label: string; value: string; } -interface FieldFormInputProps { +type FieldFormInputProps = { fieldConfig: InputField; errorMsg?: string | [string, string][]; onDataChange: ({ @@ -91,9 +97,16 @@ interface FieldFormInputProps { value: FormValue; }) => void; prefillData?: FormValue; - dropdownOptions?: DropdownOptions[]; + dropdownOptions?: DropdownOptions[] | Currency[]; disabled?: boolean; -} + autocompleteInputProps?: + | Partial + | Partial + | Partial; +} & Pick< + AutocompleteProps, + "renderOption" | "filterOptions" +>; export const FieldFormInput = ({ fieldConfig, errorMsg, @@ -101,6 +114,9 @@ export const FieldFormInput = ({ prefillData, dropdownOptions, disabled, + renderOption, + filterOptions, + autocompleteInputProps, }: FieldFormInputProps) => { const options = fieldConfig.type === "options" || @@ -214,9 +230,19 @@ export const FieldFormInput = ({ {fieldConfig.type === "autocomplete" && ( <> - - {fieldConfig.label} - + + + {fieldConfig.label} + + {fieldConfig.tooltip && ( + + + + )} + )} isOptionEqualToValue={(option, value) => @@ -248,16 +280,12 @@ export const FieldFormInput = ({ height: "40px", }, }} + renderOption={renderOption} + filterOptions={filterOptions} /> {prefillData && !dropdownOptions.find((option) => option.value === prefillData) && ( - + {fieldConfig.name === "group_id" && "The folder this was locked to has been deleted"} {fieldConfig.name === "relatedModelZUID" && @@ -326,12 +354,9 @@ export const FieldFormInput = ({ error={Boolean(errorMsg)} helperText={ errorMsg && ( - + {errorMsg} - + ) } type={fieldConfig.inputType || "text"} @@ -447,9 +472,9 @@ const KeyValueInput = ({ handleDataChanged("value", e.target?.value); }} helperText={ - + {labelErrorMsg} - + } error={Boolean(labelErrorMsg)} disabled={disabledFields.includes("value")} @@ -465,9 +490,9 @@ const KeyValueInput = ({ handleDataChanged("key", e.target?.value); }} helperText={ - + {valueErrorMsg} - + } error={Boolean(valueErrorMsg)} disabled={disabledFields.includes("key")} diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx index 810467613a..a6b20e607e 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx @@ -13,6 +13,11 @@ import { Button, Grid, Stack, + ListItem, + FilledInputProps, + InputProps, + OutlinedInputProps, + InputAdornment, } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; import { isEmpty } from "lodash"; @@ -64,6 +69,11 @@ import { DefaultValue } from "../DefaultValue"; import { CharacterLimit } from "../CharacterLimit"; import { Rules } from "./Rules"; import { MaxLengths } from "../../../../../../content-editor/src/app/components/Editor/Editor"; +import { + Currency, + currencies, +} from "../../../../../../../shell/components/FieldTypeCurrency/currencies"; +import getFlagEmoji from "../../../../../../../utility/getFlagEmoji"; type ActiveTab = "details" | "rules" | "learn"; type Params = { @@ -223,6 +233,8 @@ export const FieldForm = ({ formFields[field.name] = fieldData.settings[field.name] ?? null; } else if (field.name === "maxValue") { formFields[field.name] = fieldData.settings[field.name] ?? null; + } else if (field.name === "currency") { + formFields[field.name] = fieldData.settings?.currency ?? "USD"; } else if (field.name === "fileExtensions") { formFields[field.name] = fieldData.settings[field.name] ?? null; } else if (field.name === "fileExtensionsErrorMessage") { @@ -399,6 +411,10 @@ export const FieldForm = ({ } } + if (inputName === "currency" && !formData.currency) { + newErrorsObj[inputName] = "Please select a currency"; + } + if ( inputName === "fileExtensions" && formData.fileExtensions !== null && @@ -427,6 +443,7 @@ export const FieldForm = ({ "regexRestrictErrorMessage", "minValue", "maxValue", + "currency", "fileExtensions", "fileExtensionsErrorMessage", ].includes(inputName) @@ -588,6 +605,9 @@ export const FieldForm = ({ ...(formData.maxValue !== null && { maxValue: formData.maxValue as number, }), + ...(formData.currency !== null && { + currency: formData.currency as string, + }), ...(formData.fileExtensions && { fileExtensions: formData.fileExtensions as string[], }), @@ -790,6 +810,12 @@ export const FieldForm = ({ let dropdownOptions: DropdownOptions[]; let disabled = false; + let renderOption: any; + let filterOptions: any; + let autocompleteInputProps: + | Partial + | Partial + | Partial; if (fieldConfig.name === "relatedModelZUID") { dropdownOptions = modelsOptions; @@ -801,6 +827,73 @@ export const FieldForm = ({ disabled = isFetchingSelectedModelFields; } + if (fieldConfig.name === "currency") { + const selectedValue = currencies.find( + (currency) => currency.value === formData.currency + ); + dropdownOptions = currencies; + renderOption = (props: any, value: Currency) => ( + + + + {value.value} {value.symbol_native}   + + {value.label} + + ); + filterOptions = (options: Currency[], state: any) => { + if (state.inputValue) { + return options.filter( + (option) => + option.label + ?.toLowerCase() + .includes(state.inputValue.toLowerCase()) || + option.value + ?.toLowerCase() + .includes(state.inputValue.toLowerCase()) + ); + } else { + return options; + } + }; + autocompleteInputProps = { + startAdornment: !!selectedValue && ( + + + + {selectedValue.value} {selectedValue.symbol_native} + + + ), + }; + } + return ( ); })} diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx index 23c146ffc8..65c0dab5ee 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx @@ -81,6 +81,7 @@ export const Rules = ({ relatedFieldZUID: formData["relatedFieldZUID"] as string, }} options={formData["options"] as FieldSettingsOptions[]} + currency={(formData["currency"] as string) || "USD"} /> {(type === "text" || type === "textarea") && ( diff --git a/src/apps/schema/src/app/components/configs.ts b/src/apps/schema/src/app/components/configs.ts index ec60c38746..ec94b50998 100644 --- a/src/apps/schema/src/app/components/configs.ts +++ b/src/apps/schema/src/app/components/configs.ts @@ -502,7 +502,24 @@ const FORM_CONFIG: Record = { rules: [...COMMON_RULES], }, currency: { - details: [...COMMON_FIELDS], + details: [ + { + name: "currency", + type: "autocomplete", + label: "Currency", + required: true, + gridSize: 12, + tooltip: + "The selected currency code, symbol, and flag will be displayed for this field in the content item and can be accessed through the field settings via the API.", + placeholder: "Select a Currency", + autoFocus: true, + }, + { + ...COMMON_FIELDS[0], + autoFocus: false, + }, + ...COMMON_FIELDS.slice(1), + ], rules: [...COMMON_RULES, ...INPUT_RANGE_RULES], }, date: { diff --git a/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.js b/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.js deleted file mode 100644 index 19ccb43fea..0000000000 --- a/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.js +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState } from "react"; - -import { Input } from "@zesty-io/core/Input"; -import { Select, Option } from "@zesty-io/core/Select"; -import { currencies } from "./currencies"; - -import styles from "./FieldTypeCurrency.less"; -export const FieldTypeCurrency = React.memo(function FieldTypeCurrency(props) { - // console.log("FieldTypeCurrency:render"); - - const [monetaryValue, setMonetaryValue] = useState(props.value || "0.00"); - const [currency, setCurrency] = useState( - (props.code && currencies[props.code]) || currencies["USD"] - ); - - return ( - - ); -}); diff --git a/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.less b/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.less deleted file mode 100644 index 03c4c02fe0..0000000000 --- a/src/shell/components/FieldTypeCurrency/FieldTypeCurrency.less +++ /dev/null @@ -1,52 +0,0 @@ -@import "~@zesty-io/core/colors.less"; -@import "~@zesty-io/core/typography.less"; - -.FieldTypeCurrency { - display: flex; - flex-direction: column; - max-width: 300px; - - .FieldTypeCurrencyLabel { - display: flex; - justify-content: space-between; - margin-bottom: 3px; - font-size: @font-size-label; - span { - display: flex; - } - } - - .CurrencyFields { - display: flex; - .SelectCurrency { - width: 100px; - span > span { - background: @zesty-tab-blue; - color: @white; - border: @zesty-tab-blue; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - // &:active, - // &:focus, - // &:hover { - // span > span { - // color: #5b667d; - // } - // } - - ul { - left: 0; - min-width: 320px; - } - } - .CurrencyInput { - width: 100%; - border-radius: 0px 8px 8px 0px; - } - } -} -.CurrencyInput { - border: 1px solid #f2f4f7; -} diff --git a/src/shell/components/FieldTypeCurrency/currencies.js b/src/shell/components/FieldTypeCurrency/currencies.ts similarity index 61% rename from src/shell/components/FieldTypeCurrency/currencies.js rename to src/shell/components/FieldTypeCurrency/currencies.ts index 10102d6fa9..1630cc386c 100644 --- a/src/shell/components/FieldTypeCurrency/currencies.js +++ b/src/shell/components/FieldTypeCurrency/currencies.ts @@ -1,1064 +1,1193 @@ -export const currencies = { - USD: { +export type Currency = { + symbol: string; + label: string; + symbol_native: string; + decimal_digits: number; + rounding: number; + value: string; + name_plural: string; + countryCode: string; +}; + +export const currencies: Currency[] = [ + { symbol: " $ ", - name: "US Dollar", + label: "US Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "USD", + value: "USD", name_plural: "US dollars", + countryCode: "US", }, - CAD: { + { symbol: "CA$ ", - name: "Canadian Dollar", + label: "Canadian Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "CAD", + value: "CAD", name_plural: "Canadian dollars", + countryCode: "CA", }, - EUR: { + { symbol: " € ", - name: "Euro", + label: "Euro", symbol_native: "€", decimal_digits: 2, rounding: 0, - code: "EUR", + value: "EUR", name_plural: "euros", + countryCode: "EU", }, - AED: { + { symbol: "AED", - name: "United Arab Emirates Dirham", + label: "United Arab Emirates Dirham", symbol_native: "د.إ.‏", decimal_digits: 2, rounding: 0, - code: "AED", + value: "AED", name_plural: "UAE dirhams", + countryCode: "AE", }, - AFN: { + { symbol: "Af ", - name: "Afghan Afghani", + label: "Afghan Afghani", symbol_native: "؋", decimal_digits: 0, rounding: 0, - code: "AFN", + value: "AFN", name_plural: "Afghan Afghanis", + countryCode: "AF", }, - ALL: { + { symbol: "ALL", - name: "Albanian Lek", + label: "Albanian Lek", symbol_native: "Lek", decimal_digits: 0, rounding: 0, - code: "ALL", + value: "ALL", name_plural: "Albanian lekë", + countryCode: "AL", }, - AMD: { + { symbol: "AMD", - name: "Armenian Dram", + label: "Armenian Dram", symbol_native: "դր.", decimal_digits: 0, rounding: 0, - code: "AMD", + value: "AMD", name_plural: "Armenian drams", + countryCode: "AM", }, - ARS: { + { symbol: "AR$", - name: "Argentine Peso", + label: "Argentine Peso", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "ARS", + value: "ARS", name_plural: "Argentine pesos", + countryCode: "AR", }, - AUD: { + { symbol: "AU$", - name: "Australian Dollar", + label: "Australian Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "AUD", + value: "AUD", name_plural: "Australian dollars", + countryCode: "AU", }, - AZN: { + { symbol: "man.", - name: "Azerbaijani Manat", + label: "Azerbaijani Manat", symbol_native: "ман.", decimal_digits: 2, rounding: 0, - code: "AZN", + value: "AZN", name_plural: "Azerbaijani manats", + countryCode: "AZ", }, - BAM: { + { symbol: "KM", - name: "Bosnia-Herzegovina Convertible Mark", + label: "Bosnia-Herzegovina Convertible Mark", symbol_native: "KM", decimal_digits: 2, rounding: 0, - code: "BAM", + value: "BAM", name_plural: "Bosnia-Herzegovina convertible marks", + countryCode: "BA", }, - BDT: { + { symbol: "Tk", - name: "Bangladeshi Taka", + label: "Bangladeshi Taka", symbol_native: "৳", decimal_digits: 2, rounding: 0, - code: "BDT", + value: "BDT", name_plural: "Bangladeshi takas", + countryCode: "BD", }, - BGN: { + { symbol: "BGN", - name: "Bulgarian Lev", + label: "Bulgarian Lev", symbol_native: "лв.", decimal_digits: 2, rounding: 0, - code: "BGN", + value: "BGN", name_plural: "Bulgarian leva", + countryCode: "BG", }, - BHD: { + { symbol: "BD", - name: "Bahraini Dinar", + label: "Bahraini Dinar", symbol_native: "د.ب.‏", decimal_digits: 3, rounding: 0, - code: "BHD", + value: "BHD", name_plural: "Bahraini dinars", + countryCode: "BH", }, - BIF: { + { symbol: "FBu", - name: "Burundian Franc", + label: "Burundian Franc", symbol_native: "FBu", decimal_digits: 0, rounding: 0, - code: "BIF", + value: "BIF", name_plural: "Burundian francs", + countryCode: "BI", }, - BND: { + { symbol: "BN$", - name: "Brunei Dollar", + label: "Brunei Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "BND", + value: "BND", name_plural: "Brunei dollars", + countryCode: "BN", }, - BOB: { + { symbol: "Bs", - name: "Bolivian Boliviano", + label: "Bolivian Boliviano", symbol_native: "Bs", decimal_digits: 2, rounding: 0, - code: "BOB", + value: "BOB", name_plural: "Bolivian bolivianos", + countryCode: "BO", }, - BRL: { + { symbol: "R$", - name: "Brazilian Real", + label: "Brazilian Real", symbol_native: "R$", decimal_digits: 2, rounding: 0, - code: "BRL", + value: "BRL", name_plural: "Brazilian reals", + countryCode: "BR", }, - BWP: { + { symbol: "BWP", - name: "Botswanan Pula", + label: "Botswanan Pula", symbol_native: "P", decimal_digits: 2, rounding: 0, - code: "BWP", + value: "BWP", name_plural: "Botswanan pulas", + countryCode: "BW", }, - BYR: { + { symbol: "BYR", - name: "Belarusian Ruble", + label: "Belarusian Ruble", symbol_native: "BYR", decimal_digits: 0, rounding: 0, - code: "BYR", + value: "BYR", name_plural: "Belarusian rubles", + countryCode: "BY", }, - BZD: { + { symbol: "BZ$", - name: "Belize Dollar", + label: "Belize Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "BZD", + value: "BZD", name_plural: "Belize dollars", + countryCode: "BZ", }, - CDF: { + { symbol: "CDF", - name: "Congolese Franc", + label: "Congolese Franc", symbol_native: "FrCD", decimal_digits: 2, rounding: 0, - code: "CDF", + value: "CDF", name_plural: "Congolese francs", + countryCode: "CD", }, - CHF: { + { symbol: "CHF", - name: "Swiss Franc", + label: "Swiss Franc", symbol_native: "CHF", decimal_digits: 2, rounding: 0.05, - code: "CHF", + value: "CHF", name_plural: "Swiss francs", + countryCode: "CH", }, - CLP: { + { symbol: "CL$", - name: "Chilean Peso", + label: "Chilean Peso", symbol_native: "$", decimal_digits: 0, rounding: 0, - code: "CLP", + value: "CLP", name_plural: "Chilean pesos", + countryCode: "CL", }, - CNY: { + { symbol: "CN¥", - name: "Chinese Yuan", + label: "Chinese Yuan", symbol_native: "CN¥", decimal_digits: 2, rounding: 0, - code: "CNY", + value: "CNY", name_plural: "Chinese yuan", + countryCode: "CN", }, - COP: { + { symbol: "CO$", - name: "Colombian Peso", + label: "Colombian Peso", symbol_native: "$", decimal_digits: 0, rounding: 0, - code: "COP", + value: "COP", name_plural: "Colombian pesos", + countryCode: "CO", }, - CRC: { + { symbol: "₡", - name: "Costa Rican Colón", + label: "Costa Rican Colón", symbol_native: "₡", decimal_digits: 0, rounding: 0, - code: "CRC", + value: "CRC", name_plural: "Costa Rican colóns", + countryCode: "CR", }, - CVE: { + { symbol: "CV$", - name: "Cape Verdean Escudo", + label: "Cape Verdean Escudo", symbol_native: "CV$", decimal_digits: 2, rounding: 0, - code: "CVE", + value: "CVE", name_plural: "Cape Verdean escudos", + countryCode: "CV", }, - CZK: { + { symbol: "Kč", - name: "Czech Republic Koruna", + label: "Czech Republic Koruna", symbol_native: "Kč", decimal_digits: 2, rounding: 0, - code: "CZK", + value: "CZK", name_plural: "Czech Republic korunas", + countryCode: "CZ", }, - DJF: { + { symbol: "Fdj", - name: "Djiboutian Franc", + label: "Djiboutian Franc", symbol_native: "Fdj", decimal_digits: 0, rounding: 0, - code: "DJF", + value: "DJF", name_plural: "Djiboutian francs", + countryCode: "DJ", }, - DKK: { + { symbol: "Dkr", - name: "Danish Krone", + label: "Danish Krone", symbol_native: "kr", decimal_digits: 2, rounding: 0, - code: "DKK", + value: "DKK", name_plural: "Danish kroner", + countryCode: "DK", }, - DOP: { + { symbol: "RD$", - name: "Dominican Peso", + label: "Dominican Peso", symbol_native: "RD$", decimal_digits: 2, rounding: 0, - code: "DOP", + value: "DOP", name_plural: "Dominican pesos", + countryCode: "DO", }, - DZD: { + { symbol: "DA", - name: "Algerian Dinar", + label: "Algerian Dinar", symbol_native: "د.ج.‏", decimal_digits: 2, rounding: 0, - code: "DZD", + value: "DZD", name_plural: "Algerian dinars", + countryCode: "DZ", }, - EEK: { + { symbol: "Ekr", - name: "Estonian Kroon", + label: "Estonian Kroon", symbol_native: "kr", decimal_digits: 2, rounding: 0, - code: "EEK", + value: "EEK", name_plural: "Estonian kroons", + countryCode: "EE", }, - EGP: { + { symbol: "EGP", - name: "Egyptian Pound", + label: "Egyptian Pound", symbol_native: "ج.م.‏", decimal_digits: 2, rounding: 0, - code: "EGP", + value: "EGP", name_plural: "Egyptian pounds", + countryCode: "EG", }, - ERN: { + { symbol: "Nfk", - name: "Eritrean Nakfa", + label: "Eritrean Nakfa", symbol_native: "Nfk", decimal_digits: 2, rounding: 0, - code: "ERN", + value: "ERN", name_plural: "Eritrean nakfas", + countryCode: "ER", }, - ETB: { + { symbol: "Br", - name: "Ethiopian Birr", + label: "Ethiopian Birr", symbol_native: "Br", decimal_digits: 2, rounding: 0, - code: "ETB", + value: "ETB", name_plural: "Ethiopian birrs", + countryCode: "ET", }, - GBP: { + { symbol: "£", - name: "British Pound Sterling", + label: "British Pound Sterling", symbol_native: "£", decimal_digits: 2, rounding: 0, - code: "GBP", + value: "GBP", name_plural: "British pounds sterling", + countryCode: "GB", }, - GEL: { + { symbol: "GEL", - name: "Georgian Lari", + label: "Georgian Lari", symbol_native: "GEL", decimal_digits: 2, rounding: 0, - code: "GEL", + value: "GEL", name_plural: "Georgian laris", + countryCode: "GE", }, - GHS: { + { symbol: "GH₵", - name: "Ghanaian Cedi", + label: "Ghanaian Cedi", symbol_native: "GH₵", decimal_digits: 2, rounding: 0, - code: "GHS", + value: "GHS", name_plural: "Ghanaian cedis", + countryCode: "GH", }, - GNF: { + { symbol: "FG", - name: "Guinean Franc", + label: "Guinean Franc", symbol_native: "FG", decimal_digits: 0, rounding: 0, - code: "GNF", + value: "GNF", name_plural: "Guinean francs", + countryCode: "GN", }, - GTQ: { + { symbol: "GTQ", - name: "Guatemalan Quetzal", + label: "Guatemalan Quetzal", symbol_native: "Q", decimal_digits: 2, rounding: 0, - code: "GTQ", + value: "GTQ", name_plural: "Guatemalan quetzals", + countryCode: "GT", }, - HKD: { + { symbol: "HK$", - name: "Hong Kong Dollar", + label: "Hong Kong Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "HKD", + value: "HKD", name_plural: "Hong Kong dollars", + countryCode: "HK", }, - HNL: { + { symbol: "HNL", - name: "Honduran Lempira", + label: "Honduran Lempira", symbol_native: "L", decimal_digits: 2, rounding: 0, - code: "HNL", + value: "HNL", name_plural: "Honduran lempiras", + countryCode: "HN", }, - HRK: { + { symbol: "kn", - name: "Croatian Kuna", + label: "Croatian Kuna", symbol_native: "kn", decimal_digits: 2, rounding: 0, - code: "HRK", + value: "HRK", name_plural: "Croatian kunas", + countryCode: "HR", }, - HUF: { + { symbol: "Ft", - name: "Hungarian Forint", + label: "Hungarian Forint", symbol_native: "Ft", decimal_digits: 0, rounding: 0, - code: "HUF", + value: "HUF", name_plural: "Hungarian forints", + countryCode: "HU", }, - IDR: { + { symbol: "Rp", - name: "Indonesian Rupiah", + label: "Indonesian Rupiah", symbol_native: "Rp", decimal_digits: 0, rounding: 0, - code: "IDR", + value: "IDR", name_plural: "Indonesian rupiahs", + countryCode: "ID", }, - ILS: { + { symbol: "₪", - name: "Israeli New Sheqel", + label: "Israeli New Sheqel", symbol_native: "₪", decimal_digits: 2, rounding: 0, - code: "ILS", + value: "ILS", name_plural: "Israeli new sheqels", + countryCode: "IL", }, - INR: { + { symbol: "Rs", - name: "Indian Rupee", - symbol_native: "টকা", + label: "Indian Rupee", + symbol_native: "₹", decimal_digits: 2, rounding: 0, - code: "INR", + value: "INR", name_plural: "Indian rupees", + countryCode: "IN", }, - IQD: { + { symbol: "IQD", - name: "Iraqi Dinar", + label: "Iraqi Dinar", symbol_native: "د.ع.‏", decimal_digits: 0, rounding: 0, - code: "IQD", + value: "IQD", name_plural: "Iraqi dinars", + countryCode: "IQ", }, - IRR: { + { symbol: "IRR", - name: "Iranian Rial", + label: "Iranian Rial", symbol_native: "﷼", decimal_digits: 0, rounding: 0, - code: "IRR", + value: "IRR", name_plural: "Iranian rials", + countryCode: "IR", }, - ISK: { + { symbol: "Ikr", - name: "Icelandic Króna", + label: "Icelandic Króna", symbol_native: "kr", decimal_digits: 0, rounding: 0, - code: "ISK", + value: "ISK", name_plural: "Icelandic krónur", + countryCode: "IS", }, - JMD: { + { symbol: "J$", - name: "Jamaican Dollar", + label: "Jamaican Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "JMD", + value: "JMD", name_plural: "Jamaican dollars", + countryCode: "JM", }, - JOD: { + { symbol: "JD", - name: "Jordanian Dinar", + label: "Jordanian Dinar", symbol_native: "د.أ.‏", decimal_digits: 3, rounding: 0, - code: "JOD", + value: "JOD", name_plural: "Jordanian dinars", + countryCode: "JO", }, - JPY: { + { symbol: "¥", - name: "Japanese Yen", + label: "Japanese Yen", symbol_native: "¥", decimal_digits: 0, rounding: 0, - code: "JPY", + value: "JPY", name_plural: "Japanese yen", + countryCode: "JP", }, - KES: { + { symbol: "Ksh", - name: "Kenyan Shilling", + label: "Kenyan Shilling", symbol_native: "Ksh", decimal_digits: 2, rounding: 0, - code: "KES", + value: "KES", name_plural: "Kenyan shillings", + countryCode: "KE", }, - KHR: { + { symbol: "KHR", - name: "Cambodian Riel", + label: "Cambodian Riel", symbol_native: "៛", decimal_digits: 2, rounding: 0, - code: "KHR", + value: "KHR", name_plural: "Cambodian riels", + countryCode: "KH", }, - KMF: { + { symbol: "CF", - name: "Comorian Franc", + label: "Comorian Franc", symbol_native: "FC", decimal_digits: 0, rounding: 0, - code: "KMF", + value: "KMF", name_plural: "Comorian francs", + countryCode: "KM", }, - KRW: { + { symbol: "₩", - name: "South Korean Won", + label: "South Korean Won", symbol_native: "₩", decimal_digits: 0, rounding: 0, - code: "KRW", + value: "KRW", name_plural: "South Korean won", + countryCode: "KR", }, - KWD: { + { symbol: "KD", - name: "Kuwaiti Dinar", + label: "Kuwaiti Dinar", symbol_native: "د.ك.‏", decimal_digits: 3, rounding: 0, - code: "KWD", + value: "KWD", name_plural: "Kuwaiti dinars", + countryCode: "KW", }, - KZT: { + { symbol: "KZT", - name: "Kazakhstani Tenge", + label: "Kazakhstani Tenge", symbol_native: "тңг.", decimal_digits: 2, rounding: 0, - code: "KZT", + value: "KZT", name_plural: "Kazakhstani tenges", + countryCode: "KZ", }, - LBP: { + { symbol: "LB£", - name: "Lebanese Pound", + label: "Lebanese Pound", symbol_native: "ل.ل.‏", decimal_digits: 0, rounding: 0, - code: "LBP", + value: "LBP", name_plural: "Lebanese pounds", + countryCode: "LB", }, - LKR: { + { symbol: "SLRs", - name: "Sri Lankan Rupee", + label: "Sri Lankan Rupee", symbol_native: "SL Re", decimal_digits: 2, rounding: 0, - code: "LKR", + value: "LKR", name_plural: "Sri Lankan rupees", + countryCode: "LK", }, - LTL: { + { symbol: "Lt", - name: "Lithuanian Litas", + label: "Lithuanian Litas", symbol_native: "Lt", decimal_digits: 2, rounding: 0, - code: "LTL", + value: "LTL", name_plural: "Lithuanian litai", + countryCode: "LT", }, - LVL: { + { symbol: "Ls", - name: "Latvian Lats", + label: "Latvian Lats", symbol_native: "Ls", decimal_digits: 2, rounding: 0, - code: "LVL", + value: "LVL", name_plural: "Latvian lati", + countryCode: "LV", }, - LYD: { + { symbol: "LD", - name: "Libyan Dinar", + label: "Libyan Dinar", symbol_native: "د.ل.‏", decimal_digits: 3, rounding: 0, - code: "LYD", + value: "LYD", name_plural: "Libyan dinars", + countryCode: "LY", }, - MAD: { + { symbol: "MAD", - name: "Moroccan Dirham", + label: "Moroccan Dirham", symbol_native: "د.م.‏", decimal_digits: 2, rounding: 0, - code: "MAD", + value: "MAD", name_plural: "Moroccan dirhams", + countryCode: "MA", }, - MDL: { + { symbol: "MDL", - name: "Moldovan Leu", + label: "Moldovan Leu", symbol_native: "MDL", decimal_digits: 2, rounding: 0, - code: "MDL", + value: "MDL", name_plural: "Moldovan lei", + countryCode: "MD", }, - MGA: { + { symbol: "MGA", - name: "Malagasy Ariary", + label: "Malagasy Ariary", symbol_native: "MGA", decimal_digits: 0, rounding: 0, - code: "MGA", + value: "MGA", name_plural: "Malagasy Ariaries", + countryCode: "MG", }, - MKD: { + { symbol: "MKD", - name: "Macedonian Denar", - symbol_native: "MKD", + label: "Macedonian Denar", + symbol_native: "ден", decimal_digits: 2, rounding: 0, - code: "MKD", + value: "MKD", name_plural: "Macedonian denari", + countryCode: "MK", }, - MMK: { + { symbol: "MMK", - name: "Myanma Kyat", + label: "Myanma Kyat", symbol_native: "K", decimal_digits: 0, rounding: 0, - code: "MMK", + value: "MMK", name_plural: "Myanma kyats", + countryCode: "MM", }, - MOP: { + { symbol: "MOP$", - name: "Macanese Pataca", + label: "Macanese Pataca", symbol_native: "MOP$", decimal_digits: 2, rounding: 0, - code: "MOP", + value: "MOP", name_plural: "Macanese patacas", + countryCode: "MO", }, - MUR: { + { symbol: "MURs", - name: "Mauritian Rupee", + label: "Mauritian Rupee", symbol_native: "MURs", decimal_digits: 0, rounding: 0, - code: "MUR", + value: "MUR", name_plural: "Mauritian rupees", + countryCode: "MU", }, - MXN: { + { symbol: "MX$", - name: "Mexican Peso", + label: "Mexican Peso", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "MXN", + value: "MXN", name_plural: "Mexican pesos", + countryCode: "MX", }, - MYR: { + { symbol: "RM", - name: "Malaysian Ringgit", + label: "Malaysian Ringgit", symbol_native: "RM", decimal_digits: 2, rounding: 0, - code: "MYR", + value: "MYR", name_plural: "Malaysian ringgits", + countryCode: "MY", }, - MZN: { + { symbol: "MTn", - name: "Mozambican Metical", + label: "Mozambican Metical", symbol_native: "MTn", decimal_digits: 2, rounding: 0, - code: "MZN", + value: "MZN", name_plural: "Mozambican meticals", + countryCode: "MZ", }, - NAD: { + { symbol: "N$", - name: "Namibian Dollar", + label: "Namibian Dollar", symbol_native: "N$", decimal_digits: 2, rounding: 0, - code: "NAD", + value: "NAD", name_plural: "Namibian dollars", + countryCode: "NA", }, - NGN: { + { symbol: "₦", - name: "Nigerian Naira", + label: "Nigerian Naira", symbol_native: "₦", decimal_digits: 2, rounding: 0, - code: "NGN", + value: "NGN", name_plural: "Nigerian nairas", + countryCode: "NG", }, - NIO: { + { symbol: "C$", - name: "Nicaraguan Córdoba", + label: "Nicaraguan Córdoba", symbol_native: "C$", decimal_digits: 2, rounding: 0, - code: "NIO", + value: "NIO", name_plural: "Nicaraguan córdobas", + countryCode: "NI", }, - NOK: { + { symbol: "Nkr", - name: "Norwegian Krone", + label: "Norwegian Krone", symbol_native: "kr", decimal_digits: 2, rounding: 0, - code: "NOK", + value: "NOK", name_plural: "Norwegian kroner", + countryCode: "NO", }, - NPR: { + { symbol: "NPRs", - name: "Nepalese Rupee", + label: "Nepalese Rupee", symbol_native: "नेरू", decimal_digits: 2, rounding: 0, - code: "NPR", + value: "NPR", name_plural: "Nepalese rupees", + countryCode: "NP", }, - NZD: { + { symbol: "NZ$", - name: "New Zealand Dollar", + label: "New Zealand Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "NZD", + value: "NZD", name_plural: "New Zealand dollars", + countryCode: "NZ", }, - OMR: { + { symbol: "OMR", - name: "Omani Rial", + label: "Omani Rial", symbol_native: "ر.ع.‏", decimal_digits: 3, rounding: 0, - code: "OMR", + value: "OMR", name_plural: "Omani rials", + countryCode: "OM", }, - PAB: { + { symbol: "B/.", - name: "Panamanian Balboa", + label: "Panamanian Balboa", symbol_native: "B/.", decimal_digits: 2, rounding: 0, - code: "PAB", + value: "PAB", name_plural: "Panamanian balboas", + countryCode: "PA", }, - PEN: { + { symbol: "S/.", - name: "Peruvian Nuevo Sol", + label: "Peruvian Nuevo Sol", symbol_native: "S/.", decimal_digits: 2, rounding: 0, - code: "PEN", + value: "PEN", name_plural: "Peruvian nuevos soles", + countryCode: "PE", }, - PHP: { + { symbol: "₱", - name: "Philippine Peso", + label: "Philippine Peso", symbol_native: "₱", decimal_digits: 2, rounding: 0, - code: "PHP", + value: "PHP", name_plural: "Philippine pesos", + countryCode: "PH", }, - PKR: { + { symbol: "PKRs", - name: "Pakistani Rupee", + label: "Pakistani Rupee", symbol_native: "₨", decimal_digits: 0, rounding: 0, - code: "PKR", + value: "PKR", name_plural: "Pakistani rupees", + countryCode: "PK", }, - PLN: { + { symbol: "zł", - name: "Polish Zloty", + label: "Polish Zloty", symbol_native: "zł", decimal_digits: 2, rounding: 0, - code: "PLN", + value: "PLN", name_plural: "Polish zlotys", + countryCode: "PL", }, - PYG: { + { symbol: "₲", - name: "Paraguayan Guarani", + label: "Paraguayan Guarani", symbol_native: "₲", decimal_digits: 0, rounding: 0, - code: "PYG", + value: "PYG", name_plural: "Paraguayan guaranis", + countryCode: "PY", }, - QAR: { + { symbol: "QR", - name: "Qatari Rial", + label: "Qatari Rial", symbol_native: "ر.ق.‏", decimal_digits: 2, rounding: 0, - code: "QAR", + value: "QAR", name_plural: "Qatari rials", + countryCode: "QA", }, - RON: { + { symbol: "RON", - name: "Romanian Leu", + label: "Romanian Leu", symbol_native: "RON", decimal_digits: 2, rounding: 0, - code: "RON", + value: "RON", name_plural: "Romanian lei", + countryCode: "RO", }, - RSD: { + { symbol: "din.", - name: "Serbian Dinar", + label: "Serbian Dinar", symbol_native: "дин.", decimal_digits: 0, rounding: 0, - code: "RSD", + value: "RSD", name_plural: "Serbian dinars", + countryCode: "RS", }, - RUB: { + { symbol: "RUB", - name: "Russian Ruble", + label: "Russian Ruble", symbol_native: "руб.", decimal_digits: 2, rounding: 0, - code: "RUB", + value: "RUB", name_plural: "Russian rubles", + countryCode: "RU", }, - RWF: { + { symbol: "RWF", - name: "Rwandan Franc", + label: "Rwandan Franc", symbol_native: "FR", decimal_digits: 0, rounding: 0, - code: "RWF", + value: "RWF", name_plural: "Rwandan francs", + countryCode: "RW", }, - SAR: { + { symbol: "SR", - name: "Saudi Riyal", + label: "Saudi Riyal", symbol_native: "ر.س.‏", decimal_digits: 2, rounding: 0, - code: "SAR", + value: "SAR", name_plural: "Saudi riyals", + countryCode: "SA", }, - SDG: { + { symbol: "SDG", - name: "Sudanese Pound", + label: "Sudanese Pound", symbol_native: "SDG", decimal_digits: 2, rounding: 0, - code: "SDG", + value: "SDG", name_plural: "Sudanese pounds", + countryCode: "SD", }, - SEK: { + { symbol: "Skr", - name: "Swedish Krona", + label: "Swedish Krona", symbol_native: "kr", decimal_digits: 2, rounding: 0, - code: "SEK", + value: "SEK", name_plural: "Swedish kronor", + countryCode: "SE", }, - SGD: { + { symbol: "S$", - name: "Singapore Dollar", + label: "Singapore Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "SGD", + value: "SGD", name_plural: "Singapore dollars", + countryCode: "SG", }, - SOS: { + { symbol: "Ssh", - name: "Somali Shilling", + label: "Somali Shilling", symbol_native: "Ssh", decimal_digits: 0, rounding: 0, - code: "SOS", + value: "SOS", name_plural: "Somali shillings", + countryCode: "SO", }, - SYP: { + { symbol: "SY£", - name: "Syrian Pound", + label: "Syrian Pound", symbol_native: "ل.س.‏", decimal_digits: 0, rounding: 0, - code: "SYP", + value: "SYP", name_plural: "Syrian pounds", + countryCode: "SY", }, - THB: { + { symbol: "฿", - name: "Thai Baht", + label: "Thai Baht", symbol_native: "฿", decimal_digits: 2, rounding: 0, - code: "THB", + value: "THB", name_plural: "Thai baht", + countryCode: "TH", }, - TND: { + { symbol: "DT", - name: "Tunisian Dinar", + label: "Tunisian Dinar", symbol_native: "د.ت.‏", decimal_digits: 3, rounding: 0, - code: "TND", + value: "TND", name_plural: "Tunisian dinars", + countryCode: "TN", }, - TOP: { + { symbol: "T$", - name: "Tongan Paʻanga", + label: "Tongan Paʻanga", symbol_native: "T$", decimal_digits: 2, rounding: 0, - code: "TOP", + value: "TOP", name_plural: "Tongan paʻanga", + countryCode: "TO", }, - TRY: { + { symbol: "TL", - name: "Turkish Lira", + label: "Turkish Lira", symbol_native: "TL", decimal_digits: 2, rounding: 0, - code: "TRY", + value: "TRY", name_plural: "Turkish Lira", + countryCode: "TR", }, - TTD: { + { symbol: "TT$", - name: "Trinidad and Tobago Dollar", + label: "Trinidad and Tobago Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "TTD", + value: "TTD", name_plural: "Trinidad and Tobago dollars", + countryCode: "TT", }, - TWD: { + { symbol: "NT$", - name: "New Taiwan Dollar", + label: "New Taiwan Dollar", symbol_native: "NT$", decimal_digits: 2, rounding: 0, - code: "TWD", + value: "TWD", name_plural: "New Taiwan dollars", + countryCode: "TW", }, - TZS: { + { symbol: "TSh", - name: "Tanzanian Shilling", + label: "Tanzanian Shilling", symbol_native: "TSh", decimal_digits: 0, rounding: 0, - code: "TZS", + value: "TZS", name_plural: "Tanzanian shillings", + countryCode: "TZ", }, - UAH: { + { symbol: "₴", - name: "Ukrainian Hryvnia", + label: "Ukrainian Hryvnia", symbol_native: "₴", decimal_digits: 2, rounding: 0, - code: "UAH", + value: "UAH", name_plural: "Ukrainian hryvnias", + countryCode: "UA", }, - UGX: { + { symbol: "USh", - name: "Ugandan Shilling", + label: "Ugandan Shilling", symbol_native: "USh", decimal_digits: 0, rounding: 0, - code: "UGX", + value: "UGX", name_plural: "Ugandan shillings", + countryCode: "UG", }, - UYU: { + { symbol: "$U", - name: "Uruguayan Peso", + label: "Uruguayan Peso", symbol_native: "$", decimal_digits: 2, rounding: 0, - code: "UYU", + value: "UYU", name_plural: "Uruguayan pesos", + countryCode: "UY", }, - UZS: { + { symbol: "UZS", - name: "Uzbekistan Som", + label: "Uzbekistan Som", symbol_native: "UZS", decimal_digits: 0, rounding: 0, - code: "UZS", + value: "UZS", name_plural: "Uzbekistan som", + countryCode: "UZ", }, - VEF: { + { symbol: "Bs.F.", - name: "Venezuelan Bolívar", + label: "Venezuelan Bolívar", symbol_native: "Bs.F.", decimal_digits: 2, rounding: 0, - code: "VEF", + value: "VEF", name_plural: "Venezuelan bolívars", + countryCode: "VE", }, - VND: { + { symbol: "₫", - name: "Vietnamese Dong", + label: "Vietnamese Dong", symbol_native: "₫", decimal_digits: 0, rounding: 0, - code: "VND", + value: "VND", name_plural: "Vietnamese dong", + countryCode: "VN", }, - XAF: { + { symbol: "FCFA", - name: "CFA Franc BEAC", + label: "CFA Franc BEAC", symbol_native: "FCFA", decimal_digits: 0, rounding: 0, - code: "XAF", + value: "XAF", name_plural: "CFA francs BEAC", + countryCode: "CF", }, - XOF: { + { symbol: "CFA", - name: "CFA Franc BCEAO", + label: "CFA Franc BCEAO", symbol_native: "CFA", decimal_digits: 0, rounding: 0, - code: "XOF", + value: "XOF", name_plural: "CFA francs BCEAO", + countryCode: "CI", // We're defaulting to Ivory Coast as the country for this currency }, - YER: { + { symbol: "YR", - name: "Yemeni Rial", + label: "Yemeni Rial", symbol_native: "ر.ي.‏", decimal_digits: 0, rounding: 0, - code: "YER", + value: "YER", name_plural: "Yemeni rials", + countryCode: "YE", }, - ZAR: { + { symbol: "R", - name: "South African Rand", + label: "South African Rand", symbol_native: "R", decimal_digits: 2, rounding: 0, - code: "ZAR", + value: "ZAR", name_plural: "South African rand", + countryCode: "ZA", }, - ZMK: { + { symbol: "ZK", - name: "Zambian Kwacha", + label: "Zambian Kwacha", symbol_native: "ZK", decimal_digits: 0, rounding: 0, - code: "ZMK", + value: "ZMK", name_plural: "Zambian kwachas", + countryCode: "ZM", }, -}; +]; diff --git a/src/shell/components/FieldTypeCurrency/index.js b/src/shell/components/FieldTypeCurrency/index.js deleted file mode 100644 index ad300a9373..0000000000 --- a/src/shell/components/FieldTypeCurrency/index.js +++ /dev/null @@ -1 +0,0 @@ -export { FieldTypeCurrency } from "./FieldTypeCurrency"; diff --git a/src/shell/components/FieldTypeCurrency/index.tsx b/src/shell/components/FieldTypeCurrency/index.tsx new file mode 100644 index 0000000000..a74cad89c9 --- /dev/null +++ b/src/shell/components/FieldTypeCurrency/index.tsx @@ -0,0 +1,64 @@ +import { useMemo } from "react"; +import { TextField, Typography, Box, Stack } from "@mui/material"; + +import { currencies } from "./currencies"; +import { NumberFormatInput } from "../NumberFormatInput"; + +type FieldTypeCurrencyProps = { + name: string; + value: string; + currency: string; + error: boolean; + onChange: (value: string, name: string) => void; +}; +export const FieldTypeCurrency = ({ + name, + currency, + value, + error, + onChange, + ...otherProps +}: FieldTypeCurrencyProps) => { + const selectedCurrency = useMemo(() => { + return currencies.find((_currency) => _currency.value === currency); + }, [currency]); + + return ( + onChange(evt?.target?.value?.value, name)} + InputProps={{ + inputComponent: NumberFormatInput as any, + inputProps: { + thousandSeparator: true, + valueIsNumericString: true, + }, + startAdornment: ( + + {selectedCurrency?.symbol_native} + + ), + endAdornment: ( + + + + {selectedCurrency.value} + + + ), + }} + /> + ); +}; diff --git a/src/shell/components/FieldTypeNumber.tsx b/src/shell/components/FieldTypeNumber.tsx index aacf8169f1..5329ea7020 100644 --- a/src/shell/components/FieldTypeNumber.tsx +++ b/src/shell/components/FieldTypeNumber.tsx @@ -66,8 +66,10 @@ export const FieldTypeNumber = ({ value={value || 0} name={name} required={required} - onChange={(evt) => { - onChange(+evt.target.value?.toString()?.replace(/^0+/, "") ?? 0, name); + onChange={(evt: any) => { + const value = evt?.target?.value?.floatValue ?? 0; + + onChange(+value?.toString()?.replace(/^0+/, "") ?? 0, name); }} onKeyDown={(evt) => { if ((evt.key === "Backspace" || evt.key === "Delete") && value === 0) { diff --git a/src/shell/components/NumberFormatInput/index.tsx b/src/shell/components/NumberFormatInput/index.tsx index 6efa07b581..db7effd76a 100644 --- a/src/shell/components/NumberFormatInput/index.tsx +++ b/src/shell/components/NumberFormatInput/index.tsx @@ -3,10 +3,17 @@ import { NumericFormatProps, InputAttributes, NumericFormat, + NumberFormatValues, } from "react-number-format"; +export type NumberFormatInputEvent = { + target: { + name: string; + value: NumberFormatValues; + }; +}; type NumberFormatInputProps = { - onChange: (event: { target: { name: string; value: number } }) => void; + onChange: (event: NumberFormatInputEvent) => void; name: string; }; export const NumberFormatInput = forwardRef< @@ -23,7 +30,7 @@ export const NumberFormatInput = forwardRef< onChange({ target: { name: props.name, - value: values.floatValue || 0, + value: values, }, }); }} diff --git a/src/shell/services/types.ts b/src/shell/services/types.ts index b31e07a159..1af2f44da7 100644 --- a/src/shell/services/types.ts +++ b/src/shell/services/types.ts @@ -203,6 +203,7 @@ export interface FieldSettings { regexRestrictErrorMessage?: string; minValue?: number; maxValue?: number; + currency?: string; fileExtensions?: string[]; fileExtensionsErrorMessage?: string; } diff --git a/src/utility/getFlagEmoji.ts b/src/utility/getFlagEmoji.ts new file mode 100644 index 0000000000..dd4b9854ec --- /dev/null +++ b/src/utility/getFlagEmoji.ts @@ -0,0 +1,9 @@ +export default (countryCode: string) => { + // Convert country code to flag emoji. + // Unicode flag emojis are made up of regional indicator symbols, which are a sequence of two letters. + const baseOffset = 0x1f1e6; + return ( + String.fromCodePoint(baseOffset + (countryCode.charCodeAt(0) - 65)) + + String.fromCodePoint(baseOffset + (countryCode.charCodeAt(1) - 65)) + ); +}; From bae5cea981c690e94dfacf9677f09545bda39be0 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:32:55 +0800 Subject: [PATCH 08/16] [Schema] Currency field vqa updates (#2883) - Set max-height to 256px - Changed US Dollar to United States Dollar - Sort options alphabetically ### Demo ![image](https://github.com/user-attachments/assets/afcc1a0f-af5a-4356-8941-8ccb1a64bbd9) ![image](https://github.com/user-attachments/assets/686383fc-0d54-4f28-a9bc-d5b4d55373dd) --- .../AddFieldModal/FieldFormInput.tsx | 26 +- .../AddFieldModal/views/FieldForm.tsx | 16 +- .../FieldTypeCurrency/currencies.ts | 932 +++++++++--------- 3 files changed, 495 insertions(+), 479 deletions(-) diff --git a/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx b/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx index 500d57b262..7517f00c6d 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/FieldFormInput.tsx @@ -86,6 +86,13 @@ export interface DropdownOptions { label: string; value: string; } +export type AutocompleteConfig = { + inputProps?: + | Partial + | Partial + | Partial; + maxHeight?: number; +}; type FieldFormInputProps = { fieldConfig: InputField; errorMsg?: string | [string, string][]; @@ -99,10 +106,7 @@ type FieldFormInputProps = { prefillData?: FormValue; dropdownOptions?: DropdownOptions[] | Currency[]; disabled?: boolean; - autocompleteInputProps?: - | Partial - | Partial - | Partial; + autocompleteConfig?: AutocompleteConfig; } & Pick< AutocompleteProps, "renderOption" | "filterOptions" @@ -116,7 +120,7 @@ export const FieldFormInput = ({ disabled, renderOption, filterOptions, - autocompleteInputProps, + autocompleteConfig, }: FieldFormInputProps) => { const options = fieldConfig.type === "options" || @@ -262,7 +266,7 @@ export const FieldFormInput = ({ helperText={errorMsg} InputProps={{ ...params.InputProps, - ...autocompleteInputProps, + ...autocompleteConfig?.inputProps, }} /> )} @@ -282,6 +286,16 @@ export const FieldFormInput = ({ }} renderOption={renderOption} filterOptions={filterOptions} + slotProps={{ + paper: { + sx: { + "& .MuiAutocomplete-listbox": { + maxHeight: autocompleteConfig?.maxHeight || "40vh", + boxSizing: "border-box", + }, + }, + }, + }} /> {prefillData && !dropdownOptions.find((option) => option.value === prefillData) && ( diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx index a6b20e607e..661edcf3ff 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/FieldForm.tsx @@ -32,7 +32,11 @@ import PauseCircleOutlineRoundedIcon from "@mui/icons-material/PauseCircleOutlin import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded"; import { FieldIcon } from "../../Field/FieldIcon"; -import { FieldFormInput, DropdownOptions } from "../FieldFormInput"; +import { + FieldFormInput, + DropdownOptions, + AutocompleteConfig, +} from "../FieldFormInput"; import { useMediaRules } from "../../hooks/useMediaRules"; import { MediaRules } from "../MediaRules"; import { @@ -812,10 +816,7 @@ export const FieldForm = ({ let disabled = false; let renderOption: any; let filterOptions: any; - let autocompleteInputProps: - | Partial - | Partial - | Partial; + let autocompleteConfig: AutocompleteConfig = {}; if (fieldConfig.name === "relatedModelZUID") { dropdownOptions = modelsOptions; @@ -866,7 +867,7 @@ export const FieldForm = ({ return options; } }; - autocompleteInputProps = { + autocompleteConfig.inputProps = { startAdornment: !!selectedValue && ( ), }; + autocompleteConfig.maxHeight = 256; } return ( @@ -905,7 +907,7 @@ export const FieldForm = ({ disabled={disabled} renderOption={renderOption} filterOptions={filterOptions} - autocompleteInputProps={autocompleteInputProps} + autocompleteConfig={autocompleteConfig} /> ); })} diff --git a/src/shell/components/FieldTypeCurrency/currencies.ts b/src/shell/components/FieldTypeCurrency/currencies.ts index 1630cc386c..ab439b5265 100644 --- a/src/shell/components/FieldTypeCurrency/currencies.ts +++ b/src/shell/components/FieldTypeCurrency/currencies.ts @@ -10,46 +10,6 @@ export type Currency = { }; export const currencies: Currency[] = [ - { - symbol: " $ ", - label: "US Dollar", - symbol_native: "$", - decimal_digits: 2, - rounding: 0, - value: "USD", - name_plural: "US dollars", - countryCode: "US", - }, - { - symbol: "CA$ ", - label: "Canadian Dollar", - symbol_native: "$", - decimal_digits: 2, - rounding: 0, - value: "CAD", - name_plural: "Canadian dollars", - countryCode: "CA", - }, - { - symbol: " € ", - label: "Euro", - symbol_native: "€", - decimal_digits: 2, - rounding: 0, - value: "EUR", - name_plural: "euros", - countryCode: "EU", - }, - { - symbol: "AED", - label: "United Arab Emirates Dirham", - symbol_native: "د.إ.‏", - decimal_digits: 2, - rounding: 0, - value: "AED", - name_plural: "UAE dirhams", - countryCode: "AE", - }, { symbol: "Af ", label: "Afghan Afghani", @@ -71,14 +31,14 @@ export const currencies: Currency[] = [ countryCode: "AL", }, { - symbol: "AMD", - label: "Armenian Dram", - symbol_native: "դր.", - decimal_digits: 0, + symbol: "DA", + label: "Algerian Dinar", + symbol_native: "د.ج.‏", + decimal_digits: 2, rounding: 0, - value: "AMD", - name_plural: "Armenian drams", - countryCode: "AM", + value: "DZD", + name_plural: "Algerian dinars", + countryCode: "DZ", }, { symbol: "AR$", @@ -90,6 +50,16 @@ export const currencies: Currency[] = [ name_plural: "Argentine pesos", countryCode: "AR", }, + { + symbol: "AMD", + label: "Armenian Dram", + symbol_native: "դր.", + decimal_digits: 0, + rounding: 0, + value: "AMD", + name_plural: "Armenian drams", + countryCode: "AM", + }, { symbol: "AU$", label: "Australian Dollar", @@ -111,14 +81,14 @@ export const currencies: Currency[] = [ countryCode: "AZ", }, { - symbol: "KM", - label: "Bosnia-Herzegovina Convertible Mark", - symbol_native: "KM", - decimal_digits: 2, + symbol: "BD", + label: "Bahraini Dinar", + symbol_native: "د.ب.‏", + decimal_digits: 3, rounding: 0, - value: "BAM", - name_plural: "Bosnia-Herzegovina convertible marks", - countryCode: "BA", + value: "BHD", + name_plural: "Bahraini dinars", + countryCode: "BH", }, { symbol: "Tk", @@ -131,44 +101,24 @@ export const currencies: Currency[] = [ countryCode: "BD", }, { - symbol: "BGN", - label: "Bulgarian Lev", - symbol_native: "лв.", - decimal_digits: 2, - rounding: 0, - value: "BGN", - name_plural: "Bulgarian leva", - countryCode: "BG", - }, - { - symbol: "BD", - label: "Bahraini Dinar", - symbol_native: "د.ب.‏", - decimal_digits: 3, - rounding: 0, - value: "BHD", - name_plural: "Bahraini dinars", - countryCode: "BH", - }, - { - symbol: "FBu", - label: "Burundian Franc", - symbol_native: "FBu", + symbol: "BYR", + label: "Belarusian Ruble", + symbol_native: "BYR", decimal_digits: 0, rounding: 0, - value: "BIF", - name_plural: "Burundian francs", - countryCode: "BI", + value: "BYR", + name_plural: "Belarusian rubles", + countryCode: "BY", }, { - symbol: "BN$", - label: "Brunei Dollar", + symbol: "BZ$", + label: "Belize Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - value: "BND", - name_plural: "Brunei dollars", - countryCode: "BN", + value: "BZD", + name_plural: "Belize dollars", + countryCode: "BZ", }, { symbol: "Bs", @@ -181,14 +131,14 @@ export const currencies: Currency[] = [ countryCode: "BO", }, { - symbol: "R$", - label: "Brazilian Real", - symbol_native: "R$", + symbol: "KM", + label: "Bosnia-Herzegovina Convertible Mark", + symbol_native: "KM", decimal_digits: 2, rounding: 0, - value: "BRL", - name_plural: "Brazilian reals", - countryCode: "BR", + value: "BAM", + name_plural: "Bosnia-Herzegovina convertible marks", + countryCode: "BA", }, { symbol: "BWP", @@ -201,44 +151,104 @@ export const currencies: Currency[] = [ countryCode: "BW", }, { - symbol: "BYR", - label: "Belarusian Ruble", - symbol_native: "BYR", - decimal_digits: 0, + symbol: "R$", + label: "Brazilian Real", + symbol_native: "R$", + decimal_digits: 2, rounding: 0, - value: "BYR", - name_plural: "Belarusian rubles", - countryCode: "BY", + value: "BRL", + name_plural: "Brazilian reals", + countryCode: "BR", }, { - symbol: "BZ$", - label: "Belize Dollar", + symbol: "£", + label: "British Pound Sterling", + symbol_native: "£", + decimal_digits: 2, + rounding: 0, + value: "GBP", + name_plural: "British pounds sterling", + countryCode: "GB", + }, + { + symbol: "BN$", + label: "Brunei Dollar", symbol_native: "$", decimal_digits: 2, rounding: 0, - value: "BZD", - name_plural: "Belize dollars", - countryCode: "BZ", + value: "BND", + name_plural: "Brunei dollars", + countryCode: "BN", }, { - symbol: "CDF", - label: "Congolese Franc", - symbol_native: "FrCD", + symbol: "BGN", + label: "Bulgarian Lev", + symbol_native: "лв.", decimal_digits: 2, rounding: 0, - value: "CDF", - name_plural: "Congolese francs", - countryCode: "CD", + value: "BGN", + name_plural: "Bulgarian leva", + countryCode: "BG", }, { - symbol: "CHF", - label: "Swiss Franc", - symbol_native: "CHF", + symbol: "FBu", + label: "Burundian Franc", + symbol_native: "FBu", + decimal_digits: 0, + rounding: 0, + value: "BIF", + name_plural: "Burundian francs", + countryCode: "BI", + }, + { + symbol: "KHR", + label: "Cambodian Riel", + symbol_native: "៛", decimal_digits: 2, - rounding: 0.05, - value: "CHF", - name_plural: "Swiss francs", - countryCode: "CH", + rounding: 0, + value: "KHR", + name_plural: "Cambodian riels", + countryCode: "KH", + }, + { + symbol: "CA$ ", + label: "Canadian Dollar", + symbol_native: "$", + decimal_digits: 2, + rounding: 0, + value: "CAD", + name_plural: "Canadian dollars", + countryCode: "CA", + }, + { + symbol: "CV$", + label: "Cape Verdean Escudo", + symbol_native: "CV$", + decimal_digits: 2, + rounding: 0, + value: "CVE", + name_plural: "Cape Verdean escudos", + countryCode: "CV", + }, + { + symbol: "CFA", + label: "CFA Franc BCEAO", + symbol_native: "CFA", + decimal_digits: 0, + rounding: 0, + value: "XOF", + name_plural: "CFA francs BCEAO", + countryCode: "CI", + }, + { + symbol: "FCFA", + label: "CFA Franc BEAC", + symbol_native: "FCFA", + decimal_digits: 0, + rounding: 0, + value: "XAF", + name_plural: "CFA francs BEAC", + countryCode: "CF", }, { symbol: "CL$", @@ -270,6 +280,26 @@ export const currencies: Currency[] = [ name_plural: "Colombian pesos", countryCode: "CO", }, + { + symbol: "CF", + label: "Comorian Franc", + symbol_native: "FC", + decimal_digits: 0, + rounding: 0, + value: "KMF", + name_plural: "Comorian francs", + countryCode: "KM", + }, + { + symbol: "CDF", + label: "Congolese Franc", + symbol_native: "FrCD", + decimal_digits: 2, + rounding: 0, + value: "CDF", + name_plural: "Congolese francs", + countryCode: "CD", + }, { symbol: "₡", label: "Costa Rican Colón", @@ -281,14 +311,14 @@ export const currencies: Currency[] = [ countryCode: "CR", }, { - symbol: "CV$", - label: "Cape Verdean Escudo", - symbol_native: "CV$", + symbol: "kn", + label: "Croatian Kuna", + symbol_native: "kn", decimal_digits: 2, rounding: 0, - value: "CVE", - name_plural: "Cape Verdean escudos", - countryCode: "CV", + value: "HRK", + name_plural: "Croatian kunas", + countryCode: "HR", }, { symbol: "Kč", @@ -300,16 +330,6 @@ export const currencies: Currency[] = [ name_plural: "Czech Republic korunas", countryCode: "CZ", }, - { - symbol: "Fdj", - label: "Djiboutian Franc", - symbol_native: "Fdj", - decimal_digits: 0, - rounding: 0, - value: "DJF", - name_plural: "Djiboutian francs", - countryCode: "DJ", - }, { symbol: "Dkr", label: "Danish Krone", @@ -320,6 +340,16 @@ export const currencies: Currency[] = [ name_plural: "Danish kroner", countryCode: "DK", }, + { + symbol: "Fdj", + label: "Djiboutian Franc", + symbol_native: "Fdj", + decimal_digits: 0, + rounding: 0, + value: "DJF", + name_plural: "Djiboutian francs", + countryCode: "DJ", + }, { symbol: "RD$", label: "Dominican Peso", @@ -330,26 +360,6 @@ export const currencies: Currency[] = [ name_plural: "Dominican pesos", countryCode: "DO", }, - { - symbol: "DA", - label: "Algerian Dinar", - symbol_native: "د.ج.‏", - decimal_digits: 2, - rounding: 0, - value: "DZD", - name_plural: "Algerian dinars", - countryCode: "DZ", - }, - { - symbol: "Ekr", - label: "Estonian Kroon", - symbol_native: "kr", - decimal_digits: 2, - rounding: 0, - value: "EEK", - name_plural: "Estonian kroons", - countryCode: "EE", - }, { symbol: "EGP", label: "Egyptian Pound", @@ -370,6 +380,16 @@ export const currencies: Currency[] = [ name_plural: "Eritrean nakfas", countryCode: "ER", }, + { + symbol: "Ekr", + label: "Estonian Kroon", + symbol_native: "kr", + decimal_digits: 2, + rounding: 0, + value: "EEK", + name_plural: "Estonian kroons", + countryCode: "EE", + }, { symbol: "Br", label: "Ethiopian Birr", @@ -381,14 +401,14 @@ export const currencies: Currency[] = [ countryCode: "ET", }, { - symbol: "£", - label: "British Pound Sterling", - symbol_native: "£", + symbol: " € ", + label: "Euro", + symbol_native: "€", decimal_digits: 2, rounding: 0, - value: "GBP", - name_plural: "British pounds sterling", - countryCode: "GB", + value: "EUR", + name_plural: "euros", + countryCode: "EU", }, { symbol: "GEL", @@ -410,16 +430,6 @@ export const currencies: Currency[] = [ name_plural: "Ghanaian cedis", countryCode: "GH", }, - { - symbol: "FG", - label: "Guinean Franc", - symbol_native: "FG", - decimal_digits: 0, - rounding: 0, - value: "GNF", - name_plural: "Guinean francs", - countryCode: "GN", - }, { symbol: "GTQ", label: "Guatemalan Quetzal", @@ -431,14 +441,14 @@ export const currencies: Currency[] = [ countryCode: "GT", }, { - symbol: "HK$", - label: "Hong Kong Dollar", - symbol_native: "$", - decimal_digits: 2, + symbol: "FG", + label: "Guinean Franc", + symbol_native: "FG", + decimal_digits: 0, rounding: 0, - value: "HKD", - name_plural: "Hong Kong dollars", - countryCode: "HK", + value: "GNF", + name_plural: "Guinean francs", + countryCode: "GN", }, { symbol: "HNL", @@ -451,14 +461,14 @@ export const currencies: Currency[] = [ countryCode: "HN", }, { - symbol: "kn", - label: "Croatian Kuna", - symbol_native: "kn", + symbol: "HK$", + label: "Hong Kong Dollar", + symbol_native: "$", decimal_digits: 2, rounding: 0, - value: "HRK", - name_plural: "Croatian kunas", - countryCode: "HR", + value: "HKD", + name_plural: "Hong Kong dollars", + countryCode: "HK", }, { symbol: "Ft", @@ -471,24 +481,14 @@ export const currencies: Currency[] = [ countryCode: "HU", }, { - symbol: "Rp", - label: "Indonesian Rupiah", - symbol_native: "Rp", + symbol: "Ikr", + label: "Icelandic Króna", + symbol_native: "kr", decimal_digits: 0, rounding: 0, - value: "IDR", - name_plural: "Indonesian rupiahs", - countryCode: "ID", - }, - { - symbol: "₪", - label: "Israeli New Sheqel", - symbol_native: "₪", - decimal_digits: 2, - rounding: 0, - value: "ILS", - name_plural: "Israeli new sheqels", - countryCode: "IL", + value: "ISK", + name_plural: "Icelandic krónur", + countryCode: "IS", }, { symbol: "Rs", @@ -501,14 +501,14 @@ export const currencies: Currency[] = [ countryCode: "IN", }, { - symbol: "IQD", - label: "Iraqi Dinar", - symbol_native: "د.ع.‏", + symbol: "Rp", + label: "Indonesian Rupiah", + symbol_native: "Rp", decimal_digits: 0, rounding: 0, - value: "IQD", - name_plural: "Iraqi dinars", - countryCode: "IQ", + value: "IDR", + name_plural: "Indonesian rupiahs", + countryCode: "ID", }, { symbol: "IRR", @@ -521,14 +521,24 @@ export const currencies: Currency[] = [ countryCode: "IR", }, { - symbol: "Ikr", - label: "Icelandic Króna", - symbol_native: "kr", + symbol: "IQD", + label: "Iraqi Dinar", + symbol_native: "د.ع.‏", decimal_digits: 0, rounding: 0, - value: "ISK", - name_plural: "Icelandic krónur", - countryCode: "IS", + value: "IQD", + name_plural: "Iraqi dinars", + countryCode: "IQ", + }, + { + symbol: "₪", + label: "Israeli New Sheqel", + symbol_native: "₪", + decimal_digits: 2, + rounding: 0, + value: "ILS", + name_plural: "Israeli new sheqels", + countryCode: "IL", }, { symbol: "J$", @@ -540,6 +550,16 @@ export const currencies: Currency[] = [ name_plural: "Jamaican dollars", countryCode: "JM", }, + { + symbol: "¥", + label: "Japanese Yen", + symbol_native: "¥", + decimal_digits: 0, + rounding: 0, + value: "JPY", + name_plural: "Japanese yen", + countryCode: "JP", + }, { symbol: "JD", label: "Jordanian Dinar", @@ -551,14 +571,14 @@ export const currencies: Currency[] = [ countryCode: "JO", }, { - symbol: "¥", - label: "Japanese Yen", - symbol_native: "¥", - decimal_digits: 0, + symbol: "KZT", + label: "Kazakhstani Tenge", + symbol_native: "тңг.", + decimal_digits: 2, rounding: 0, - value: "JPY", - name_plural: "Japanese yen", - countryCode: "JP", + value: "KZT", + name_plural: "Kazakhstani tenges", + countryCode: "KZ", }, { symbol: "Ksh", @@ -570,36 +590,6 @@ export const currencies: Currency[] = [ name_plural: "Kenyan shillings", countryCode: "KE", }, - { - symbol: "KHR", - label: "Cambodian Riel", - symbol_native: "៛", - decimal_digits: 2, - rounding: 0, - value: "KHR", - name_plural: "Cambodian riels", - countryCode: "KH", - }, - { - symbol: "CF", - label: "Comorian Franc", - symbol_native: "FC", - decimal_digits: 0, - rounding: 0, - value: "KMF", - name_plural: "Comorian francs", - countryCode: "KM", - }, - { - symbol: "₩", - label: "South Korean Won", - symbol_native: "₩", - decimal_digits: 0, - rounding: 0, - value: "KRW", - name_plural: "South Korean won", - countryCode: "KR", - }, { symbol: "KD", label: "Kuwaiti Dinar", @@ -611,14 +601,14 @@ export const currencies: Currency[] = [ countryCode: "KW", }, { - symbol: "KZT", - label: "Kazakhstani Tenge", - symbol_native: "тңг.", + symbol: "Ls", + label: "Latvian Lats", + symbol_native: "Ls", decimal_digits: 2, rounding: 0, - value: "KZT", - name_plural: "Kazakhstani tenges", - countryCode: "KZ", + value: "LVL", + name_plural: "Latvian lati", + countryCode: "LV", }, { symbol: "LB£", @@ -630,36 +620,6 @@ export const currencies: Currency[] = [ name_plural: "Lebanese pounds", countryCode: "LB", }, - { - symbol: "SLRs", - label: "Sri Lankan Rupee", - symbol_native: "SL Re", - decimal_digits: 2, - rounding: 0, - value: "LKR", - name_plural: "Sri Lankan rupees", - countryCode: "LK", - }, - { - symbol: "Lt", - label: "Lithuanian Litas", - symbol_native: "Lt", - decimal_digits: 2, - rounding: 0, - value: "LTL", - name_plural: "Lithuanian litai", - countryCode: "LT", - }, - { - symbol: "Ls", - label: "Latvian Lats", - symbol_native: "Ls", - decimal_digits: 2, - rounding: 0, - value: "LVL", - name_plural: "Latvian lati", - countryCode: "LV", - }, { symbol: "LD", label: "Libyan Dinar", @@ -671,34 +631,24 @@ export const currencies: Currency[] = [ countryCode: "LY", }, { - symbol: "MAD", - label: "Moroccan Dirham", - symbol_native: "د.م.‏", + symbol: "Lt", + label: "Lithuanian Litas", + symbol_native: "Lt", decimal_digits: 2, rounding: 0, - value: "MAD", - name_plural: "Moroccan dirhams", - countryCode: "MA", + value: "LTL", + name_plural: "Lithuanian litai", + countryCode: "LT", }, { - symbol: "MDL", - label: "Moldovan Leu", - symbol_native: "MDL", + symbol: "MOP$", + label: "Macanese Pataca", + symbol_native: "MOP$", decimal_digits: 2, rounding: 0, - value: "MDL", - name_plural: "Moldovan lei", - countryCode: "MD", - }, - { - symbol: "MGA", - label: "Malagasy Ariary", - symbol_native: "MGA", - decimal_digits: 0, - rounding: 0, - value: "MGA", - name_plural: "Malagasy Ariaries", - countryCode: "MG", + value: "MOP", + name_plural: "Macanese patacas", + countryCode: "MO", }, { symbol: "MKD", @@ -711,24 +661,24 @@ export const currencies: Currency[] = [ countryCode: "MK", }, { - symbol: "MMK", - label: "Myanma Kyat", - symbol_native: "K", + symbol: "MGA", + label: "Malagasy Ariary", + symbol_native: "MGA", decimal_digits: 0, rounding: 0, - value: "MMK", - name_plural: "Myanma kyats", - countryCode: "MM", + value: "MGA", + name_plural: "Malagasy Ariaries", + countryCode: "MG", }, { - symbol: "MOP$", - label: "Macanese Pataca", - symbol_native: "MOP$", + symbol: "RM", + label: "Malaysian Ringgit", + symbol_native: "RM", decimal_digits: 2, rounding: 0, - value: "MOP", - name_plural: "Macanese patacas", - countryCode: "MO", + value: "MYR", + name_plural: "Malaysian ringgits", + countryCode: "MY", }, { symbol: "MURs", @@ -751,14 +701,24 @@ export const currencies: Currency[] = [ countryCode: "MX", }, { - symbol: "RM", - label: "Malaysian Ringgit", - symbol_native: "RM", + symbol: "MDL", + label: "Moldovan Leu", + symbol_native: "MDL", decimal_digits: 2, rounding: 0, - value: "MYR", - name_plural: "Malaysian ringgits", - countryCode: "MY", + value: "MDL", + name_plural: "Moldovan lei", + countryCode: "MD", + }, + { + symbol: "MAD", + label: "Moroccan Dirham", + symbol_native: "د.م.‏", + decimal_digits: 2, + rounding: 0, + value: "MAD", + name_plural: "Moroccan dirhams", + countryCode: "MA", }, { symbol: "MTn", @@ -770,6 +730,16 @@ export const currencies: Currency[] = [ name_plural: "Mozambican meticals", countryCode: "MZ", }, + { + symbol: "MMK", + label: "Myanma Kyat", + symbol_native: "K", + decimal_digits: 0, + rounding: 0, + value: "MMK", + name_plural: "Myanma kyats", + countryCode: "MM", + }, { symbol: "N$", label: "Namibian Dollar", @@ -781,14 +751,34 @@ export const currencies: Currency[] = [ countryCode: "NA", }, { - symbol: "₦", - label: "Nigerian Naira", - symbol_native: "₦", + symbol: "NPRs", + label: "Nepalese Rupee", + symbol_native: "नेरू", decimal_digits: 2, rounding: 0, - value: "NGN", - name_plural: "Nigerian nairas", - countryCode: "NG", + value: "NPR", + name_plural: "Nepalese rupees", + countryCode: "NP", + }, + { + symbol: "NT$", + label: "New Taiwan Dollar", + symbol_native: "NT$", + decimal_digits: 2, + rounding: 0, + value: "TWD", + name_plural: "New Taiwan dollars", + countryCode: "TW", + }, + { + symbol: "NZ$", + label: "New Zealand Dollar", + symbol_native: "$", + decimal_digits: 2, + rounding: 0, + value: "NZD", + name_plural: "New Zealand dollars", + countryCode: "NZ", }, { symbol: "C$", @@ -801,34 +791,24 @@ export const currencies: Currency[] = [ countryCode: "NI", }, { - symbol: "Nkr", - label: "Norwegian Krone", - symbol_native: "kr", - decimal_digits: 2, - rounding: 0, - value: "NOK", - name_plural: "Norwegian kroner", - countryCode: "NO", - }, - { - symbol: "NPRs", - label: "Nepalese Rupee", - symbol_native: "नेरू", + symbol: "₦", + label: "Nigerian Naira", + symbol_native: "₦", decimal_digits: 2, rounding: 0, - value: "NPR", - name_plural: "Nepalese rupees", - countryCode: "NP", + value: "NGN", + name_plural: "Nigerian nairas", + countryCode: "NG", }, { - symbol: "NZ$", - label: "New Zealand Dollar", - symbol_native: "$", + symbol: "Nkr", + label: "Norwegian Krone", + symbol_native: "kr", decimal_digits: 2, rounding: 0, - value: "NZD", - name_plural: "New Zealand dollars", - countryCode: "NZ", + value: "NOK", + name_plural: "Norwegian kroner", + countryCode: "NO", }, { symbol: "OMR", @@ -840,6 +820,16 @@ export const currencies: Currency[] = [ name_plural: "Omani rials", countryCode: "OM", }, + { + symbol: "PKRs", + label: "Pakistani Rupee", + symbol_native: "₨", + decimal_digits: 0, + rounding: 0, + value: "PKR", + name_plural: "Pakistani rupees", + countryCode: "PK", + }, { symbol: "B/.", label: "Panamanian Balboa", @@ -850,6 +840,16 @@ export const currencies: Currency[] = [ name_plural: "Panamanian balboas", countryCode: "PA", }, + { + symbol: "₲", + label: "Paraguayan Guarani", + symbol_native: "₲", + decimal_digits: 0, + rounding: 0, + value: "PYG", + name_plural: "Paraguayan guaranis", + countryCode: "PY", + }, { symbol: "S/.", label: "Peruvian Nuevo Sol", @@ -870,16 +870,6 @@ export const currencies: Currency[] = [ name_plural: "Philippine pesos", countryCode: "PH", }, - { - symbol: "PKRs", - label: "Pakistani Rupee", - symbol_native: "₨", - decimal_digits: 0, - rounding: 0, - value: "PKR", - name_plural: "Pakistani rupees", - countryCode: "PK", - }, { symbol: "zł", label: "Polish Zloty", @@ -890,16 +880,6 @@ export const currencies: Currency[] = [ name_plural: "Polish zlotys", countryCode: "PL", }, - { - symbol: "₲", - label: "Paraguayan Guarani", - symbol_native: "₲", - decimal_digits: 0, - rounding: 0, - value: "PYG", - name_plural: "Paraguayan guaranis", - countryCode: "PY", - }, { symbol: "QR", label: "Qatari Rial", @@ -920,16 +900,6 @@ export const currencies: Currency[] = [ name_plural: "Romanian lei", countryCode: "RO", }, - { - symbol: "din.", - label: "Serbian Dinar", - symbol_native: "дин.", - decimal_digits: 0, - rounding: 0, - value: "RSD", - name_plural: "Serbian dinars", - countryCode: "RS", - }, { symbol: "RUB", label: "Russian Ruble", @@ -961,24 +931,14 @@ export const currencies: Currency[] = [ countryCode: "SA", }, { - symbol: "SDG", - label: "Sudanese Pound", - symbol_native: "SDG", - decimal_digits: 2, - rounding: 0, - value: "SDG", - name_plural: "Sudanese pounds", - countryCode: "SD", - }, - { - symbol: "Skr", - label: "Swedish Krona", - symbol_native: "kr", - decimal_digits: 2, + symbol: "din.", + label: "Serbian Dinar", + symbol_native: "дин.", + decimal_digits: 0, rounding: 0, - value: "SEK", - name_plural: "Swedish kronor", - countryCode: "SE", + value: "RSD", + name_plural: "Serbian dinars", + countryCode: "RS", }, { symbol: "S$", @@ -1000,6 +960,66 @@ export const currencies: Currency[] = [ name_plural: "Somali shillings", countryCode: "SO", }, + { + symbol: "R", + label: "South African Rand", + symbol_native: "R", + decimal_digits: 2, + rounding: 0, + value: "ZAR", + name_plural: "South African rand", + countryCode: "ZA", + }, + { + symbol: "₩", + label: "South Korean Won", + symbol_native: "₩", + decimal_digits: 0, + rounding: 0, + value: "KRW", + name_plural: "South Korean won", + countryCode: "KR", + }, + { + symbol: "SLRs", + label: "Sri Lankan Rupee", + symbol_native: "SL Re", + decimal_digits: 2, + rounding: 0, + value: "LKR", + name_plural: "Sri Lankan rupees", + countryCode: "LK", + }, + { + symbol: "SDG", + label: "Sudanese Pound", + symbol_native: "SDG", + decimal_digits: 2, + rounding: 0, + value: "SDG", + name_plural: "Sudanese pounds", + countryCode: "SD", + }, + { + symbol: "Skr", + label: "Swedish Krona", + symbol_native: "kr", + decimal_digits: 2, + rounding: 0, + value: "SEK", + name_plural: "Swedish kronor", + countryCode: "SE", + }, + { + symbol: "CHF", + label: "Swiss Franc", + symbol_native: "CHF", + decimal_digits: 2, + rounding: 0.05, + value: "CHF", + name_plural: "Swiss francs", + countryCode: "CH", + }, { symbol: "SY£", label: "Syrian Pound", @@ -1010,6 +1030,16 @@ export const currencies: Currency[] = [ name_plural: "Syrian pounds", countryCode: "SY", }, + { + symbol: "TSh", + label: "Tanzanian Shilling", + symbol_native: "TSh", + decimal_digits: 0, + rounding: 0, + value: "TZS", + name_plural: "Tanzanian shillings", + countryCode: "TZ", + }, { symbol: "฿", label: "Thai Baht", @@ -1020,16 +1050,6 @@ export const currencies: Currency[] = [ name_plural: "Thai baht", countryCode: "TH", }, - { - symbol: "DT", - label: "Tunisian Dinar", - symbol_native: "د.ت.‏", - decimal_digits: 3, - rounding: 0, - value: "TND", - name_plural: "Tunisian dinars", - countryCode: "TN", - }, { symbol: "T$", label: "Tongan Paʻanga", @@ -1040,16 +1060,6 @@ export const currencies: Currency[] = [ name_plural: "Tongan paʻanga", countryCode: "TO", }, - { - symbol: "TL", - label: "Turkish Lira", - symbol_native: "TL", - decimal_digits: 2, - rounding: 0, - value: "TRY", - name_plural: "Turkish Lira", - countryCode: "TR", - }, { symbol: "TT$", label: "Trinidad and Tobago Dollar", @@ -1061,24 +1071,34 @@ export const currencies: Currency[] = [ countryCode: "TT", }, { - symbol: "NT$", - label: "New Taiwan Dollar", - symbol_native: "NT$", + symbol: "DT", + label: "Tunisian Dinar", + symbol_native: "د.ت.‏", + decimal_digits: 3, + rounding: 0, + value: "TND", + name_plural: "Tunisian dinars", + countryCode: "TN", + }, + { + symbol: "TL", + label: "Turkish Lira", + symbol_native: "TL", decimal_digits: 2, rounding: 0, - value: "TWD", - name_plural: "New Taiwan dollars", - countryCode: "TW", + value: "TRY", + name_plural: "Turkish Lira", + countryCode: "TR", }, { - symbol: "TSh", - label: "Tanzanian Shilling", - symbol_native: "TSh", + symbol: "USh", + label: "Ugandan Shilling", + symbol_native: "USh", decimal_digits: 0, rounding: 0, - value: "TZS", - name_plural: "Tanzanian shillings", - countryCode: "TZ", + value: "UGX", + name_plural: "Ugandan shillings", + countryCode: "UG", }, { symbol: "₴", @@ -1091,14 +1111,24 @@ export const currencies: Currency[] = [ countryCode: "UA", }, { - symbol: "USh", - label: "Ugandan Shilling", - symbol_native: "USh", - decimal_digits: 0, + symbol: "AED", + label: "United Arab Emirates Dirham", + symbol_native: "د.إ.‏", + decimal_digits: 2, rounding: 0, - value: "UGX", - name_plural: "Ugandan shillings", - countryCode: "UG", + value: "AED", + name_plural: "UAE dirhams", + countryCode: "AE", + }, + { + symbol: " $ ", + label: "United States Dollar", + symbol_native: "$", + decimal_digits: 2, + rounding: 0, + value: "USD", + name_plural: "US dollars", + countryCode: "US", }, { symbol: "$U", @@ -1140,26 +1170,6 @@ export const currencies: Currency[] = [ name_plural: "Vietnamese dong", countryCode: "VN", }, - { - symbol: "FCFA", - label: "CFA Franc BEAC", - symbol_native: "FCFA", - decimal_digits: 0, - rounding: 0, - value: "XAF", - name_plural: "CFA francs BEAC", - countryCode: "CF", - }, - { - symbol: "CFA", - label: "CFA Franc BCEAO", - symbol_native: "CFA", - decimal_digits: 0, - rounding: 0, - value: "XOF", - name_plural: "CFA francs BCEAO", - countryCode: "CI", // We're defaulting to Ivory Coast as the country for this currency - }, { symbol: "YR", label: "Yemeni Rial", @@ -1170,16 +1180,6 @@ export const currencies: Currency[] = [ name_plural: "Yemeni rials", countryCode: "YE", }, - { - symbol: "R", - label: "South African Rand", - symbol_native: "R", - decimal_digits: 2, - rounding: 0, - value: "ZAR", - name_plural: "South African rand", - countryCode: "ZA", - }, { symbol: "ZK", label: "Zambian Kwacha", From 8c0f3ac8e3f0ce2ea67bdc5be6981a75cde0883e Mon Sep 17 00:00:00 2001 From: Andres Galindo Date: Mon, 29 Jul 2024 15:54:43 -0700 Subject: [PATCH 09/16] vqa fixes media schema rules (#2890) --- .../components/AddFieldModal/MediaRules.tsx | 25 +++++++++++++--- .../components/AddFieldModal/views/Rules.tsx | 30 +++++++++---------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx b/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx index eb6b8140f3..6660a1c11d 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx @@ -152,12 +152,19 @@ export const MediaRules = ({ newInputValue: string, ruleName: string ) => { - const formattedInput = "." + newInputValue.replace(/\./g, ""); - setInputValue(formattedInput); + const formattedInput = newInputValue.trim().toLowerCase(); + if (formattedInput && formattedInput[0] !== ".") { + setInputValue(`.${formattedInput}`); + } else { + setInputValue(formattedInput); + } }; const handleKeyDown = (event: any, ruleName: string) => { - if (event.key === "Enter" || event.key === "," || event.key === " ") { + if ( + (event.key === "Enter" || event.key === "," || event.key === " ") && + inputValue + ) { event.preventDefault(); const newOption = inputValue.toLowerCase().trim(); if ( @@ -171,12 +178,22 @@ export const MediaRules = ({ }); setInputValue(""); } + } else if (event.key === "Backspace" && !inputValue) { + const newTags = [...(fieldData[ruleName] as string[])]; + newTags.pop(); + if (!newTags.length) { + setExtensionsError(true); + } + onDataChange({ + inputName: ruleName, + value: newTags, + }); } }; const handleDelete = (option: string, ruleName: string) => { const newTags = (fieldData[ruleName] as string[]).filter( - (item) => item !== option + (item) => item.trim() !== option.trim() ); if (!newTags.length) { setExtensionsError(true); diff --git a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx index 65c0dab5ee..a92b5caf27 100644 --- a/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx +++ b/src/apps/schema/src/app/components/AddFieldModal/views/Rules.tsx @@ -48,21 +48,6 @@ export const Rules = ({ return ( - {type === "images" && ( - - )} - + {type === "images" && ( + + )} + {(type === "text" || type === "textarea") && ( <> Date: Tue, 30 Jul 2024 08:20:56 +0800 Subject: [PATCH 10/16] [Content] Show image placeholder in publish modal dialog (#2888) Resolves #2837 ![image](https://github.com/user-attachments/assets/c30864de-d1e0-4c11-b660-7f9bb9974f0f) --- .../app/views/ItemList/DialogContentItem.tsx | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/apps/content-editor/src/app/views/ItemList/DialogContentItem.tsx b/src/apps/content-editor/src/app/views/ItemList/DialogContentItem.tsx index 3c3772ca65..4fafb2948b 100644 --- a/src/apps/content-editor/src/app/views/ItemList/DialogContentItem.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/DialogContentItem.tsx @@ -5,6 +5,7 @@ import { ListItemText, ListItemAvatar, Avatar, + Stack, } from "@mui/material"; import { ContentItem } from "../../../../../../shell/services/types"; import { useGetContentModelFieldsQuery } from "../../../../../../shell/services/instance"; @@ -16,6 +17,7 @@ import { import { useSelector } from "react-redux"; import { AppState } from "../../../../../../shell/store/types"; import { useMemo } from "react"; +import { ImageRounded } from "@mui/icons-material"; export type DialogContentItemProps = { item: ContentItem; @@ -50,26 +52,44 @@ export const DialogContentItem = ({ item }: DialogContentItemProps) => { return ( - - + theme.palette.grey[100], + }} + src={heroImage} + imgProps={{ + style: { + objectFit: "contain", + }, + }} + > + + NA + + + + ) : ( + theme.palette.grey[100], - }} - src={heroImage} - imgProps={{ - style: { - objectFit: "contain", - }, }} > - - NA - - - + + + )} Date: Mon, 29 Jul 2024 19:04:33 -0700 Subject: [PATCH 11/16] Hide publish based on permissions on code app (#2887) --- .../components/EditorActions/EditorActions.js | 16 +++++++----- .../src/app/components/FileList/FileList.js | 25 ++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/apps/code-editor/src/app/components/FileActions/components/EditorActions/EditorActions.js b/src/apps/code-editor/src/app/components/FileActions/components/EditorActions/EditorActions.js index d2f6d33212..ec1caf87bb 100644 --- a/src/apps/code-editor/src/app/components/FileActions/components/EditorActions/EditorActions.js +++ b/src/apps/code-editor/src/app/components/FileActions/components/EditorActions/EditorActions.js @@ -4,7 +4,9 @@ import { Save } from "./Save"; import { Publish } from "./Publish"; import styles from "./EditorActions.less"; +import { usePermission } from "../../../../../../../../shell/hooks/use-permissions"; export const EditorActions = memo(function EditorActions(props) { + const canPublish = usePermission("PUBLISH"); return (
- + {canPublish && ( + + )}
); }); diff --git a/src/apps/code-editor/src/app/components/FileList/FileList.js b/src/apps/code-editor/src/app/components/FileList/FileList.js index f7c85c180c..823999426a 100644 --- a/src/apps/code-editor/src/app/components/FileList/FileList.js +++ b/src/apps/code-editor/src/app/components/FileList/FileList.js @@ -17,9 +17,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCloudUploadAlt } from "@fortawesome/free-solid-svg-icons"; import { resolvePathPart, publishFile } from "../../../store/files"; import { collapseNavItem } from "../../../store/navCode"; - +import { usePermission } from "../../../../../../shell/hooks/use-permissions"; import styles from "./FileList.less"; export const FileList = memo(function FileList(props) { + const canPublish = usePermission("PUBLISH"); // const [branch, setBranch] = useState(props.branch); const [shownFiles, setShownFiles] = useState( props.navCode.tree.sort(byLabel) @@ -47,14 +48,20 @@ export const FileList = memo(function FileList(props) { }; const actions = [ - !file.isLive} - onClick={(file) => props.dispatch(publishFile(file.ZUID, file.status))} - />, + ...(canPublish + ? [ + !file.isLive} + onClick={(file) => + props.dispatch(publishFile(file.ZUID, file.status)) + } + />, + ] + : []), ]; return ( From 361736c60d91fc5d3878b41d81257f8b1a6dea66 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Wed, 31 Jul 2024 07:46:22 +0800 Subject: [PATCH 12/16] [Content] Do not store pinned columns to local storage (#2892) Prevents multipage table pinned columns from being stored in local storage Resolves #2824 #### Demo [Screencast from Tuesday, 30 July, 2024 09:05:20 AM PST.webm](https://github.com/user-attachments/assets/07281341-7b91-45f9-9f99-245b16d83242) --- .../src/app/views/ItemList/ItemListTable.tsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx index 273c3771a9..0ad398ebda 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx @@ -16,6 +16,7 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF, useGridApiRef, GridInitialState, + GridPinnedColumns, } from "@mui/x-data-grid-pro"; import { memo, useCallback, useLayoutEffect, useMemo, useState } from "react"; import { ContentItem } from "../../../../../../shell/services/types"; @@ -246,6 +247,7 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { const history = useHistory(); const { stagedChanges } = useStagedChanges(); const [selectedItems, setSelectedItems] = useSelectedItems(); + const [pinnedColumns, setPinnedColumns] = useState({}); const { data: fields } = useGetContentModelFieldsQuery(modelZUID); @@ -266,18 +268,11 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { ); setInitialState( - stateFromLocalStorage - ? JSON.parse(stateFromLocalStorage) - : { - pinnedColumns: { - left: [ - GRID_CHECKBOX_SELECTION_COL_DEF.field, - "version", - fields?.[0]?.name, - ], - }, - } + stateFromLocalStorage ? JSON.parse(stateFromLocalStorage) : {} ); + setPinnedColumns({ + left: ["__check__", "version", fields?.[0]?.name], + }); window.addEventListener("beforeunload", saveSnapshot); @@ -352,6 +347,10 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { loading={loading} rows={rows} columns={[...columns, ...METADATA_COLUMNS]} + pinnedColumns={pinnedColumns} + onPinnedColumnsChange={(newPinnedColumns) => + setPinnedColumns(newPinnedColumns) + } rowHeight={54} hideFooter onRowClick={(row) => { From b88aa6602f05bc2b8d2267adf6e5a698613ceb60 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Wed, 31 Jul 2024 08:58:05 +0800 Subject: [PATCH 13/16] [Content] Multipage table advanced sorting (#2884) - Adds created by and zuid sorting - Rename some columns for consistency - Add table column header sorting - Sync sorting pill button and column header sorting status - Save sort order to local storage Resolves #2836 Resolves #2845 --- .../content-editor/src/app/ContentEditor.js | 5 +- .../app/views/ItemList/ItemListFilters.tsx | 128 +++++++++++++----- .../src/app/views/ItemList/ItemListTable.tsx | 30 ++-- .../app/views/ItemList/TableSortProvider.tsx | 47 +++++++ .../src/app/views/ItemList/index.tsx | 111 ++++++++++++--- .../components/CascadingMenuItem/index.tsx | 44 +++++- 6 files changed, 300 insertions(+), 65 deletions(-) create mode 100644 src/apps/content-editor/src/app/views/ItemList/TableSortProvider.tsx diff --git a/src/apps/content-editor/src/app/ContentEditor.js b/src/apps/content-editor/src/app/ContentEditor.js index 24a5e88ca3..3d22f11e63 100644 --- a/src/apps/content-editor/src/app/ContentEditor.js +++ b/src/apps/content-editor/src/app/ContentEditor.js @@ -31,6 +31,7 @@ import Analytics from "./views/Analytics"; import { ResizableContainer } from "../../../../shell/components/ResizeableContainer"; import { StagedChangesProvider } from "./views/ItemList/StagedChangesContext"; import { SelectedItemsProvider } from "./views/ItemList/SelectedItemsContext"; +import { TableSortProvider } from "./views/ItemList/TableSortProvider"; // Makes sure that other apps using legacy theme does not get affected with the palette let customTheme = createTheme(legacyTheme, { @@ -174,7 +175,9 @@ export default function ContentEditor() { render={() => ( - + + + )} diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx index b791dae062..f796b02817 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx @@ -1,12 +1,23 @@ -import { Box, Menu, MenuItem, Button, Typography } from "@mui/material"; +import { + Box, + Menu, + MenuItem, + Button, + Typography, + MenuList, + ListItemText, +} from "@mui/material"; import { DateFilter, FilterButton, UserFilter, } from "../../../../../../shell/components/Filters"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, useContext } from "react"; import { useParams } from "../../../../../../shell/hooks/useParams"; -import { ArrowDropDownOutlined } from "@mui/icons-material"; +import { + ArrowDropDownOutlined, + ChevronRightOutlined, +} from "@mui/icons-material"; import { useGetContentModelFieldsQuery, useGetLangsQuery, @@ -14,12 +25,14 @@ import { import { useDateFilterParams } from "../../../../../../shell/hooks/useDateFilterParams"; import { useGetUsersQuery } from "../../../../../../shell/services/accounts"; import { useParams as useRouterParams } from "react-router"; +import { CascadingMenuItem } from "../../../../../../shell/components/CascadingMenuItem"; +import { TableSortContext } from "./TableSortProvider"; const SORT_ORDER = { - dateSaved: "Date Saved", - datePublished: "Date Published", - dateCreated: "Date Created", - status: "Status", + lastSaved: "Last Saved", + lastPublished: "Last Published", + createdOn: "Date Created", + version: "Status", } as const; const STATUS_FILTER = { @@ -87,6 +100,9 @@ export const ItemListFilters = () => { const { data: users } = useGetUsersQuery(); const { data: fields, isFetching: isFieldsFetching } = useGetContentModelFieldsQuery(modelZUID); + const [sortModel, setSortModel] = useContext(TableSortContext); + + const activeSortOrder = sortModel?.[0]?.field; const userOptions = useMemo(() => { return users?.map((user) => ({ @@ -97,17 +113,49 @@ export const ItemListFilters = () => { })); }, [users]); + const handleUpdateSortOrder = (sortType: string) => { + setAnchorEl({ + currentTarget: null, + id: "", + }); + + setSortModel([ + { + field: sortType, + sort: "desc", + }, + ]); + }; + + const getButtonText = (activeSortOrder: string) => { + if (!activeSortOrder) { + return SORT_ORDER.lastSaved; + } + + if (activeSortOrder === "createdBy") { + return "Created By"; + } + + if (activeSortOrder === "zuid") { + return "ZUID"; + } + + if (SORT_ORDER.hasOwnProperty(activeSortOrder)) { + return SORT_ORDER[activeSortOrder as keyof typeof SORT_ORDER]; + } + + const fieldLabel = fields?.find( + (field) => field.name === activeSortOrder + )?.label; + return fieldLabel; + }; + return ( field.name === params.get("sort")) - ?.label) ?? - SORT_ORDER.dateSaved - }`} + buttonText={`Sort: ${getButtonText(activeSortOrder)}`} onOpenMenu={(event: React.MouseEvent) => { setAnchorEl({ currentTarget: event.currentTarget, @@ -134,23 +182,46 @@ export const ItemListFilters = () => { > {Object.entries(SORT_ORDER).map(([key, value]) => ( { - setParams(key, "sort"); - setAnchorEl({ - currentTarget: null, - id: "", - }); - }} + onClick={() => handleUpdateSortOrder(key)} selected={ - key === "dateSaved" - ? !params.get("sort") || params.get("sort") === key - : params.get("sort") === key + key === "lastSaved" + ? !activeSortOrder || activeSortOrder === "lastSaved" + : activeSortOrder === key } > {value} ))} + + More + + + } + PaperProps={{ + sx: { + width: 240, + }, + }} + > + + handleUpdateSortOrder("createdBy")} + > + Created By + + handleUpdateSortOrder("zuid")} + > + ZUID + + + { ?.map((field) => ( { - setParams(field.name, "sort"); - setAnchorEl({ - currentTarget: null, - id: "", - }); - }} - selected={params.get("sort") === field.name} + onClick={() => handleUpdateSortOrder(field.name)} + selected={activeSortOrder === field.name} > {field.label} @@ -217,6 +282,7 @@ export const ItemListFilters = () => { > {Object.entries(STATUS_FILTER).map(([key, value]) => ( { setParams(key, "statusFilter"); diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx index 0ad398ebda..50fb6fb7ea 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx @@ -16,9 +16,17 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF, useGridApiRef, GridInitialState, + GridComparatorFn, GridPinnedColumns, } from "@mui/x-data-grid-pro"; -import { memo, useCallback, useLayoutEffect, useMemo, useState } from "react"; +import { + memo, + useCallback, + useLayoutEffect, + useMemo, + useState, + useContext, +} from "react"; import { ContentItem } from "../../../../../../shell/services/types"; import { useStagedChanges } from "./StagedChangesContext"; import { OneToManyCell } from "./TableCells/OneToManyCell"; @@ -32,6 +40,8 @@ import { currencies } from "../../../../../../shell/components/FieldTypeCurrency import { Currency } from "../../../../../../shell/components/FieldTypeCurrency/currencies"; import { ImageCell } from "./TableCells/ImageCell"; import { SingleRelationshipCell } from "./TableCells/SingleRelationshipCell"; +import { useParams } from "../../../../../../shell/hooks/useParams"; +import { TableSortContext } from "./TableSortProvider"; type ItemListTableProps = { loading: boolean; @@ -66,15 +76,13 @@ const METADATA_COLUMNS = [ field: "createdBy", headerName: "Created By", width: 240, - sortable: false, filterable: false, renderCell: (params: GridRenderCellParams) => , }, { field: "createdOn", - headerName: "Created On", + headerName: "Date Created", width: 200, - sortable: false, filterable: false, valueGetter: (params: any) => params.row?.meta?.createdAt, }, @@ -83,7 +91,6 @@ const METADATA_COLUMNS = [ field: "lastSaved", headerName: "Last Saved", width: 200, - sortable: false, filterable: false, valueGetter: (params: any) => params.row?.web?.updatedAt, }, @@ -91,7 +98,6 @@ const METADATA_COLUMNS = [ field: "lastPublished", headerName: "Last Published", width: 200, - sortable: false, filterable: false, valueGetter: (params: any) => params.row?.publishing?.publishAt, }, @@ -99,7 +105,6 @@ const METADATA_COLUMNS = [ field: "zuid", headerName: "ZUID", width: 200, - sortable: false, filterable: false, valueGetter: (params: any) => params.row?.meta?.ZUID, }, @@ -247,6 +252,8 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { const history = useHistory(); const { stagedChanges } = useStagedChanges(); const [selectedItems, setSelectedItems] = useSelectedItems(); + const [params, setParams] = useParams(); + const [sortModel, setSortModel] = useContext(TableSortContext); const [pinnedColumns, setPinnedColumns] = useState({}); const { data: fields } = useGetContentModelFieldsQuery(modelZUID); @@ -288,7 +295,7 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { field: "version", headerName: "Vers.", width: 59, - sortable: false, + sortable: true, filterable: false, renderCell: (params: GridRenderCellParams) => ( @@ -303,7 +310,6 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { ?.map((field) => ({ field: field.name, headerName: field.label, - sortable: false, filterable: false, valueGetter: (params: any) => { if (field.datatype === "currency") { @@ -395,6 +401,12 @@ export const ItemListTable = memo(({ loading, rows }: ItemListTableProps) => { checkboxSelection disableSelectionOnClick initialState={initialState} + sortingOrder={["desc", "asc", null]} + sortModel={sortModel} + sortingMode="server" + onSortModelChange={(newSortModel) => { + setSortModel(newSortModel); + }} onSelectionModelChange={(newSelection) => setSelectedItems(newSelection)} selectionModel={ stagedChanges && Object.keys(stagedChanges)?.length ? [] : selectedItems diff --git a/src/apps/content-editor/src/app/views/ItemList/TableSortProvider.tsx b/src/apps/content-editor/src/app/views/ItemList/TableSortProvider.tsx new file mode 100644 index 0000000000..46af57c1a4 --- /dev/null +++ b/src/apps/content-editor/src/app/views/ItemList/TableSortProvider.tsx @@ -0,0 +1,47 @@ +import { useState, createContext, useLayoutEffect } from "react"; +import { GridSortModel, GridSortItem } from "@mui/x-data-grid-pro"; +import { useParams as useRouterParams } from "react-router"; + +type TableSortContextType = [ + GridSortModel, + (newSortModel: GridSortModel) => void +]; +export const TableSortContext = createContext([ + [], + () => {}, +]); + +type TableSortProviderType = { + children?: React.ReactNode; +}; +export const TableSortProvider = ({ children }: TableSortProviderType) => { + const [sortModel, setSortModel] = useState([]); + const { modelZUID } = useRouterParams<{ modelZUID: string }>(); + + useLayoutEffect(() => { + if (!modelZUID) return; + + const stateFromLocalStorage = localStorage?.getItem( + `${modelZUID}-dataGridState` + ); + + if (stateFromLocalStorage) { + const { sortModel: sortModelFromLocalStorage } = JSON.parse( + stateFromLocalStorage + )?.sorting; + + if ( + Array.isArray(sortModelFromLocalStorage) && + sortModelFromLocalStorage?.length + ) { + setSortModel(sortModelFromLocalStorage); + } + } + }, [modelZUID]); + + return ( + + {children} + + ); +}; diff --git a/src/apps/content-editor/src/app/views/ItemList/index.tsx b/src/apps/content-editor/src/app/views/ItemList/index.tsx index f3ee800eaa..15b77667ab 100644 --- a/src/apps/content-editor/src/app/views/ItemList/index.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/index.tsx @@ -9,7 +9,7 @@ import { import { theme } from "@zesty-io/material"; import { ItemListEmpty } from "./ItemListEmpty"; import { ItemListActions } from "./ItemListActions"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState, useContext } from "react"; import { SearchRounded, RestartAltRounded } from "@mui/icons-material"; import noSearchResults from "../../../../../../../public/images/noSearchResults.svg"; import { ItemListFilters } from "./ItemListFilters"; @@ -31,6 +31,7 @@ import { ContentItemWithDirtyAndPublishing, } from "../../../../../../shell/services/types"; import { fetchItems } from "../../../../../../shell/store/content"; +import { TableSortContext } from "./TableSortProvider"; const formatDateTime = (source: string) => { const dateObj = new Date(source); @@ -97,11 +98,12 @@ export const ItemList = () => { const [isModelItemsFetching, setIsModelItemsFetching] = useState(true); + const [sortModel] = useContext(TableSortContext); const { stagedChanges } = useStagedChanges(); const [selectedItems] = useSelectedItems(); const searchRef = useRef(null); const search = params.get("search"); - const sort = params.get("sort"); + // const sort = params.get("sort"); const statusFilter = params.get("statusFilter"); const dateFilter = useMemo(() => { return { @@ -243,9 +245,11 @@ export const ItemList = () => { }, [items, allItems, fields, users, isFieldsFetching, isUsersFetching]); const sortedAndFilteredItems = useMemo(() => { + const sort = sortModel?.[0]?.field; + const sortOrder = sortModel?.[0]?.sort; let clonedItems = [...processedItems]; clonedItems?.sort((a: any, b: any) => { - if (!sort || sort === "dateSaved") { + if (!sort || sort === "lastSaved") { const dateA = new Date(a.web.createdAt).getTime(); const dateB = new Date(b.web.createdAt).getTime(); @@ -254,9 +258,9 @@ export const ItemList = () => { } else if (!b.web.createdAt) { return 1; } else { - return dateB - dateA; + return sortOrder === "asc" ? dateA - dateB : dateB - dateA; } - } else if (sort === "datePublished") { + } else if (sort === "lastPublished") { // Handle undefined publishAt by setting a default far-future date for sorting purposes let dateA = a?.scheduling?.publishAt || a?.publishing?.publishAt; @@ -265,13 +269,20 @@ export const ItemList = () => { let dateB = b?.scheduling?.publishAt || b?.publishing?.publishAt; dateB = dateB ? new Date(dateB).getTime() : Number.NEGATIVE_INFINITY; - return dateB - dateA; - } else if (sort === "dateCreated") { + return sortOrder === "asc" ? dateA - dateB : dateB - dateA; + } else if (sort === "createdOn") { + if (sortOrder === "asc") { + return ( + new Date(a.meta.createdAt).getTime() - + new Date(b.meta.createdAt).getTime() + ); + } + return ( new Date(b.meta.createdAt).getTime() - new Date(a.meta.createdAt).getTime() ); - } else if (sort === "status") { + } else if (sort === "version") { const aIsPublished = a?.publishing?.publishAt; const bIsPublished = b?.publishing?.publishAt; @@ -291,6 +302,13 @@ export const ItemList = () => { // Items with only publish date if (aIsPublished && !aIsScheduled && bIsPublished && !bIsScheduled) { + if (sortOrder === "asc") { + return ( + new Date(aIsPublished).getTime() - + new Date(bIsPublished).getTime() + ); + } + return ( new Date(bIsPublished).getTime() - new Date(aIsPublished).getTime() ); // Both have only published date, sort by publish date descending @@ -302,6 +320,13 @@ export const ItemList = () => { // Items with scheduled date (and also publish date) if (aIsScheduled && bIsScheduled) { + if (sortOrder === "asc") { + return ( + new Date(bIsScheduled).getTime() - + new Date(aIsScheduled).getTime() + ); + } + return ( new Date(aIsScheduled).getTime() - new Date(bIsScheduled).getTime() ); // Both are scheduled, sort by scheduled date ascending @@ -313,6 +338,13 @@ export const ItemList = () => { // Items with neither publish nor schedule dates if (aIsPublished && bIsPublished) { + if (sortOrder === "asc") { + return ( + new Date(aIsPublished).getTime() - + new Date(bIsPublished).getTime() + ); + } + return ( new Date(bIsPublished).getTime() - new Date(aIsPublished).getTime() ); // Both are published, sort by publish date descending @@ -323,13 +355,36 @@ export const ItemList = () => { } return 0; // Neither are published or scheduled + } else if (sort === "createdBy") { + const userA = a?.meta?.createdByUserName; + const userB = b?.meta?.createdByUserName; + + if (!userA) { + return 1; + } else if (!userB) { + return -1; + } else { + return sortOrder === "asc" + ? userB.localeCompare(userA) + : userA.localeCompare(userB); + } + } else if (sort === "zuid") { + return sortOrder === "asc" + ? b.meta?.ZUID?.localeCompare(a.meta?.ZUID) + : a.meta?.ZUID?.localeCompare(b.meta?.ZUID); } else if (fields?.find((field) => field.name === sort)) { const dataType = fields?.find((field) => field.name === sort)?.datatype; if (typeof a.data[sort] === "number") { if (a.data[sort] == null) return 1; if (b.data[sort] == null) return -1; - return dataType === "sort" + if (dataType === "sort") { + return sortOrder === "asc" + ? a.data[sort] - b.data[sort] + : b.data[sort] - a.data[sort]; + } + + return sortOrder === "asc" ? a.data[sort] - b.data[sort] : b.data[sort] - a.data[sort]; } @@ -339,22 +394,38 @@ export const ItemList = () => { } else if (!b.data[sort]) { return -1; } else { - return ( - new Date(b.data[sort]).getTime() - - new Date(a.data[sort]).getTime() - ); + return sortOrder === "asc" + ? new Date(a.data[sort]).getTime() - + new Date(b.data[sort]).getTime() + : new Date(b.data[sort]).getTime() - + new Date(a.data[sort]).getTime(); + } + } + + if (dataType === "yes_no") { + if (!a.data[sort]) { + return 1; + } else if (!b.data[sort]) { + return -1; + } else { + return sortOrder === "asc" ? a - b : b - a; } } + const aValue = dataType === "images" ? a.data[sort]?.filename : a.data[sort]; const bValue = dataType === "images" ? b.data[sort]?.filename : b.data[sort]; - return aValue?.trim()?.localeCompare(bValue?.trim()); + + return sortOrder === "asc" + ? bValue?.trim()?.localeCompare(aValue?.trim()) + : aValue?.trim()?.localeCompare(bValue?.trim()); } else { - return ( - new Date(b.meta.updatedAt).getTime() - - new Date(a.meta.updatedAt).getTime() - ); + return sortOrder === "asc" + ? new Date(a.meta.updatedAt).getTime() - + new Date(b.meta.updatedAt).getTime() + : new Date(b.meta.updatedAt).getTime() - + new Date(a.meta.updatedAt).getTime(); } }); if (search) { @@ -422,7 +493,7 @@ export const ItemList = () => { // filter items by all fields return clonedItems; - }, [processedItems, search, sort, statusFilter, dateFilter, userFilter]); + }, [processedItems, search, sortModel, statusFilter, dateFilter, userFilter]); return ( @@ -534,7 +605,7 @@ export const ItemList = () => { {!sortedAndFilteredItems?.length && !isModelItemsFetching && !search && - (sort || + (!!sortModel?.length || statusFilter || dateFilter?.preset || dateFilter?.from || diff --git a/src/shell/components/CascadingMenuItem/index.tsx b/src/shell/components/CascadingMenuItem/index.tsx index b929507ad7..abd0bdce44 100644 --- a/src/shell/components/CascadingMenuItem/index.tsx +++ b/src/shell/components/CascadingMenuItem/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from "react"; +import React, { FC, useEffect, useState } from "react"; import { MenuItem, MenuItemProps, @@ -23,11 +23,37 @@ export const CascadingMenuItem: FC = ({ ...props }) => { const [anchorEl, setAnchorEl] = useState(null); + const [isChildHovered, setIsChildHovered] = useState(false); + const [isParentHovered, setIsParentHovered] = useState(false); + + /** Note: This essentially adds a small delay to allow a user to move their mouse + * to the child component instead of just immediately closing it outright + */ + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + if (!isParentHovered) { + timeoutId = setTimeout(() => { + if (!isChildHovered) { + setAnchorEl(null); + } + }, 100); + } + + return () => { + clearTimeout(timeoutId); + }; + }, [isParentHovered, isChildHovered]); return ( setAnchorEl(evt.currentTarget)} - onMouseLeave={() => setAnchorEl(null)} + onMouseEnter={(evt) => { + setAnchorEl(evt.currentTarget); + setIsParentHovered(true); + }} + onMouseLeave={() => { + setIsParentHovered(false); + }} sx={{ // HACK: Prevents the menu item to be in active style state when the sub-menu is opened. "&.MuiMenuItem-root": { @@ -51,7 +77,17 @@ export const CascadingMenuItem: FC = ({ }} {...PopperProps} > - + { + setIsChildHovered(true); + }} + onMouseLeave={() => { + setIsChildHovered(false); + setAnchorEl(null); + }} + > {children} From d49cc5fdde5a24669ef7201085e7d4d7e56efcc6 Mon Sep 17 00:00:00 2001 From: Nar -- <28705606+finnar-bin@users.noreply.github.com> Date: Thu, 1 Aug 2024 04:09:02 +0800 Subject: [PATCH 14/16] [Content] Filter button icon update & show api endpoints on hover (#2896) Resolves #2889 Resolves #2830 ### Demo [Screencast from Wednesday, 31 July, 2024 10:52:37 AM PST.webm](https://github.com/user-attachments/assets/74116615-c945-4943-9482-fa41404b854d) --- .../src/app/components/APIEndpoints.tsx | 89 +++++++++++ .../components/ItemEditHeader/MoreMenu.tsx | 143 ++++-------------- .../app/views/ItemList/ItemListActions.tsx | 137 ++++------------- .../app/views/ItemList/ItemListFilters.tsx | 4 +- .../app/components/Controls/DateFilter.tsx | 4 +- src/shell/components/Filters/FilterButton.tsx | 4 +- 6 files changed, 147 insertions(+), 234 deletions(-) create mode 100644 src/apps/content-editor/src/app/components/APIEndpoints.tsx diff --git a/src/apps/content-editor/src/app/components/APIEndpoints.tsx b/src/apps/content-editor/src/app/components/APIEndpoints.tsx new file mode 100644 index 0000000000..52b43eff1d --- /dev/null +++ b/src/apps/content-editor/src/app/components/APIEndpoints.tsx @@ -0,0 +1,89 @@ +import { useSelector } from "react-redux"; +import { useParams } from "react-router"; +import { + MenuList, + MenuItem, + ListItemIcon, + Typography, + Chip, +} from "@mui/material"; +import { DesignServicesRounded, VisibilityRounded } from "@mui/icons-material"; + +import { AppState } from "../../../../../shell/store/types"; +import { ContentItem } from "../../../../../shell/services/types"; +import { useGetDomainsQuery } from "../../../../../shell/services/accounts"; +import { ApiType } from "../../../../schema/src/app/components/ModelApi"; + +type APIEndpointsProps = { + type: Extract; +}; +export const APIEndpoints = ({ type }: APIEndpointsProps) => { + const { itemZUID } = useParams<{ + itemZUID: string; + }>(); + const item = useSelector( + (state: AppState) => state.content[itemZUID] as ContentItem + ); + const instance = useSelector((state: AppState) => state.instance); + const { data: domains } = useGetDomainsQuery(); + + const apiTypeEndpointMap: Partial> = { + "quick-access": `/-/instant/${itemZUID}.json`, + "site-generators": item ? `/${item?.web?.path}/?toJSON` : "/?toJSON", + }; + + const liveDomain = domains?.find((domain) => domain.branch == "live"); + + return ( + + { + window.open( + // @ts-expect-error config not typed + `${CONFIG.URL_PREVIEW_PROTOCOL}${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[type]}`, + "_blank" + ); + }} + > + + + + + {/* @ts-expect-error config not typed */} + {`${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[type]}`} + + + + {liveDomain && ( + { + window.open( + `https://${liveDomain.domain}${apiTypeEndpointMap[type]}`, + "_blank" + ); + }} + > + + + + + {`${liveDomain.domain}${apiTypeEndpointMap[type]}`} + + + + )} + + ); +}; diff --git a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/MoreMenu.tsx b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/MoreMenu.tsx index 0ff94721c6..30c7f04297 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/MoreMenu.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/MoreMenu.tsx @@ -1,10 +1,8 @@ import { - Chip, IconButton, ListItemIcon, Menu, MenuItem, - Typography, Tooltip, } from "@mui/material"; import { @@ -16,23 +14,18 @@ import { CodeRounded, DeleteRounded, CheckRounded, - DesignServicesRounded, - VisibilityRounded, KeyboardArrowRightRounded, } from "@mui/icons-material"; import { useState } from "react"; import { Database } from "@zesty-io/material"; import { useHistory, useParams } from "react-router"; -import { useSelector } from "react-redux"; -import { AppState } from "../../../../../../../../shell/store/types"; -import { ContentItem } from "../../../../../../../../shell/services/types"; import { DuplicateItemDialog } from "./DuplicateItemDialog"; -import { ApiType } from "../../../../../../../schema/src/app/components/ModelApi"; -import { useGetDomainsQuery } from "../../../../../../../../shell/services/accounts"; import { useFilePath } from "../../../../../../../../shell/hooks/useFilePath"; import { DeleteItemDialog } from "./DeleteItemDialog"; import { useGetContentModelsQuery } from "../../../../../../../../shell/services/instance"; import { usePermission } from "../../../../../../../../shell/hooks/use-permissions"; +import { CascadingMenuItem } from "../../../../../../../../shell/components/CascadingMenuItem"; +import { APIEndpoints } from "../../../../components/APIEndpoints"; export const MoreMenu = () => { const { modelZUID, itemZUID } = useParams<{ @@ -42,17 +35,8 @@ export const MoreMenu = () => { const [anchorEl, setAnchorEl] = useState(null); const [isCopied, setIsCopied] = useState(false); const [showDuplicateItemDialog, setShowDuplicateItemDialog] = useState(false); - const [showApiEndpoints, setShowApiEndpoints] = useState( - null - ); const [showDeleteItemDialog, setShowDeleteItemDialog] = useState(false); - const [apiEndpointType, setApiEndpointType] = useState("quick-access"); const history = useHistory(); - const item = useSelector( - (state: AppState) => state.content[itemZUID] as ContentItem - ); - const instance = useSelector((state: AppState) => state.instance); - const { data: domains } = useGetDomainsQuery(); const codePath = useFilePath(modelZUID); const { data: contentModels } = useGetContentModelsQuery(); const type = @@ -73,13 +57,6 @@ export const MoreMenu = () => { }); }; - const apiTypeEndpointMap: Partial> = { - "quick-access": `/-/instant/${itemZUID}.json`, - "site-generators": item ? `/${item?.web?.path}/?toJSON` : "/?toJSON", - }; - - const liveDomain = domains?.find((domain) => domain.branch == "live"); - return ( <> { Copy ZUID - { - setShowApiEndpoints(event.currentTarget); - setApiEndpointType("quick-access"); - }} + + + + + View Quick Access API + + + } > - - - - View Quick Access API - - + + {type !== "dataset" && ( - { - setShowApiEndpoints(event.currentTarget); - setApiEndpointType("site-generators"); - }} + + + + + View Site Generators API + + + } > - - - - View Site Generators API - - + + )} { @@ -201,76 +180,6 @@ export const MoreMenu = () => { onClose={() => setShowDuplicateItemDialog(false)} /> )} - { - setShowApiEndpoints(null); - }} - > - { - setShowApiEndpoints(null); - window.open( - // @ts-expect-error config not typed - `${CONFIG.URL_PREVIEW_PROTOCOL}${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[apiEndpointType]}`, - "_blank" - ); - }} - > - - - - - {/* @ts-expect-error config not typed */} - {`${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[apiEndpointType]}`} - - - - {liveDomain && ( - { - setShowApiEndpoints(null); - window.open( - `https://${liveDomain.domain}${ - apiTypeEndpointMap[ - apiEndpointType as keyof typeof apiTypeEndpointMap - ] - }`, - "_blank" - ); - }} - > - - - - - {`${liveDomain.domain}${ - apiTypeEndpointMap[ - apiEndpointType as keyof typeof apiTypeEndpointMap - ] - }`} - - - - )} - {showDeleteItemDialog && ( setShowDeleteItemDialog(false)} /> )} diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx index ff38b2eff9..be99600cdd 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListActions.tsx @@ -6,8 +6,6 @@ import { Menu, MenuItem, ListItemIcon, - Typography, - Chip, } from "@mui/material"; import { Database, IconButton } from "@zesty-io/material"; import { @@ -21,8 +19,6 @@ import { BoltRounded, KeyboardArrowRightRounded, DataObjectRounded, - VisibilityRounded, - DesignServicesRounded, } from "@mui/icons-material"; import { forwardRef, useState, useCallback } from "react"; import { useHistory, useParams as useRouterParams } from "react-router"; @@ -30,34 +26,21 @@ import { useFilePath } from "../../../../../../shell/hooks/useFilePath"; import { useParams } from "../../../../../../shell/hooks/useParams"; import { debounce } from "lodash"; import { useGetContentModelsQuery } from "../../../../../../shell/services/instance"; -import { useGetDomainsQuery } from "../../../../../../shell/services/accounts"; -import { ApiType } from "../../../../../schema/src/app/components/ModelApi"; -import { AppState } from "../../../../../../shell/store/types"; -import { useSelector } from "react-redux"; +import { CascadingMenuItem } from "../../../../../../shell/components/CascadingMenuItem"; +import { APIEndpoints } from "../../components/APIEndpoints"; export const ItemListActions = forwardRef((props, ref) => { const { modelZUID } = useRouterParams<{ modelZUID: string }>(); const { data: contentModels } = useGetContentModelsQuery(); - const { data: domains } = useGetDomainsQuery(); const history = useHistory(); const [anchorEl, setAnchorEl] = useState(null); const codePath = useFilePath(modelZUID); const [isCopied, setIsCopied] = useState(false); const [params, setParams] = useParams(); const [searchTerm, setSearchTerm] = useState(params.get("search") || ""); - const instance = useSelector((state: AppState) => state.instance); - const [showApiEndpoints, setShowApiEndpoints] = useState( - null - ); - const [apiEndpointType, setApiEndpointType] = useState("quick-access"); const isDataset = contentModels?.find((model) => model.ZUID === modelZUID)?.type === "dataset"; - const apiTypeEndpointMap: Partial> = { - "quick-access": `/-/instant/${modelZUID}.json`, - "site-generators": "/?toJSON", - }; - const liveDomain = domains?.find((domain) => domain.branch == "live"); const handleCopyClick = (data: string) => { navigator?.clipboard @@ -141,31 +124,33 @@ export const ItemListActions = forwardRef((props, ref) => { Copy ZUID - { - setShowApiEndpoints(event.currentTarget); - setApiEndpointType("quick-access"); - }} + + + + + View Quick Access API + + + } > - - - - View Quick Access API - - + + {!isDataset && ( - { - setShowApiEndpoints(event.currentTarget); - setApiEndpointType("site-generators"); - }} + + + + + View Site Generators API + + + } > - - - - View Site Generators API - - + + )} { )}
- { - setShowApiEndpoints(null); - }} - > - { - setShowApiEndpoints(null); - window.open( - // @ts-expect-error config not typed - `${CONFIG.URL_PREVIEW_PROTOCOL}${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[apiEndpointType]}`, - "_blank" - ); - }} - > - - - - - {/* @ts-expect-error config not typed */} - {`${instance.randomHashID}${CONFIG.URL_PREVIEW}${apiTypeEndpointMap[apiEndpointType]}`} - - - - {liveDomain && ( - { - setShowApiEndpoints(null); - window.open( - `https://${liveDomain.domain}${ - apiTypeEndpointMap[ - apiEndpointType as keyof typeof apiTypeEndpointMap - ] - }`, - "_blank" - ); - }} - > - - - - - {`${liveDomain.domain}${ - apiTypeEndpointMap[ - apiEndpointType as keyof typeof apiTypeEndpointMap - ] - }`} - - - - )} - { size="small" variant="outlined" color="inherit" - endIcon={} + endIcon={} onClick={(e) => setAnchorEl({ currentTarget: e.currentTarget, diff --git a/src/apps/media/src/app/components/Controls/DateFilter.tsx b/src/apps/media/src/app/components/Controls/DateFilter.tsx index abbf11235c..000ceca456 100644 --- a/src/apps/media/src/app/components/Controls/DateFilter.tsx +++ b/src/apps/media/src/app/components/Controls/DateFilter.tsx @@ -7,7 +7,7 @@ import MenuItem from "@mui/material/MenuItem"; import Menu from "@mui/material/Menu"; import Typography from "@mui/material/Typography"; -import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; +import KeyboardArrowDownRoundedIcon from "@mui/icons-material/KeyboardArrowDownRounded"; import CloseRounded from "@mui/icons-material/CloseRounded"; import CheckIcon from "@mui/icons-material/Check"; import Divider from "@mui/material/Divider"; @@ -98,7 +98,7 @@ export const DateRangeFilter: FC = () => { const inactiveButton = ( + + + ); + } + + return ( + <> + + + + + + + + Replace File: + +   + {originalFile?.filename} + + + + + The original file will be deleted and replaced by its new file. This + action cannot be undone and the file cannot be recovered. The file + will retain its URL and ZUID. + + + + + + + + { + setNewFile(evt.target.files[0]); + }} + hidden + accept={acceptedExtension} + style={{ display: "none" }} + /> + + ); +}; diff --git a/src/apps/media/src/app/components/FileModal/index.tsx b/src/apps/media/src/app/components/FileModal/index.tsx index e726957cd5..282c6877d0 100644 --- a/src/apps/media/src/app/components/FileModal/index.tsx +++ b/src/apps/media/src/app/components/FileModal/index.tsx @@ -18,6 +18,7 @@ import { useGetFileQuery } from "../../../../../../shell/services/mediaManager"; import { OTFEditor } from "./OTFEditor"; import { File } from "../../../../../../shell/services/types"; import { useParams } from "../../../../../../shell/hooks/useParams"; +import { ReplaceFileModal } from "./ReplaceFileModal"; const styledModal = { position: "absolute", @@ -48,6 +49,8 @@ export const FileModal: FC = ({ const location = useLocation(); const { data, isLoading, isError, isFetching } = useGetFileQuery(fileId); const [showEdit, setShowEdit] = useState(false); + const [showReplaceFileModal, setShowReplaceFileModal] = useState(false); + const [fileToReplace, setFileToReplace] = useState(null); const [params, setParams] = useParams(); const [adjacentFiles, setAdjacentFiles] = useState({ prevFile: null, @@ -150,128 +153,145 @@ export const FileModal: FC = ({ }; }, []); + if (isFetching || (!data && !isError)) { + return ( + + + + ); + } + + if (showReplaceFileModal) { + return ( + setShowReplaceFileModal(false)} + onClose={handleCloseModal} + /> + ); + } + + if (!data) { + return <>; + } + return ( - <> - {data && !isError && !isFetching ? ( - + {adjacentFiles.nextFile && ( + { + handleArrow(adjacentFiles.nextFile); + }} + sx={{ + position: "absolute", + right: -72, + top: "50%", }} > - {adjacentFiles.nextFile && ( - { - handleArrow(adjacentFiles.nextFile); - }} - sx={{ - position: "absolute", - right: -72, - top: "50%", - }} - > - - - )} - {adjacentFiles.prevFile && ( - - { - handleArrow(adjacentFiles.prevFile); - }} - sx={{ - position: "absolute", - left: -72, - top: "50%", - }} - > - - - - )} - + + )} + {adjacentFiles.prevFile && ( + + { + handleArrow(adjacentFiles.prevFile); + }} sx={{ - display: "flex", - justifyContent: "space-between", - p: 0, - overflow: "hidden", + position: "absolute", + left: -72, + top: "50%", }} > - {/* */} - - - - - - {showEdit ? ( - - ) : ( - - )} - - {/* */} - - - ) : isFetching || (!data && !isError) ? ( - + + + )} + + {/* */} + - - - ) : ( - <> - )} - + + + + + {showEdit ? ( + + ) : ( + { + setShowReplaceFileModal(true); + }} + /> + )} + + {/* */} + + ); }; diff --git a/src/apps/media/src/app/components/Thumbnail/ThumbnailContent.tsx b/src/apps/media/src/app/components/Thumbnail/ThumbnailContent.tsx index 6ce30ef6a5..009ff0de1e 100644 --- a/src/apps/media/src/app/components/Thumbnail/ThumbnailContent.tsx +++ b/src/apps/media/src/app/components/Thumbnail/ThumbnailContent.tsx @@ -17,16 +17,20 @@ interface Props { filename: string; onFilenameChange?: (value: string) => void; onTitleChange?: (value: string) => void; - isEditable?: boolean; isSelected?: boolean; + isFilenameEditable?: boolean; + isTitleEditable?: boolean; + title?: string; } export const ThumbnailContent: FC = ({ filename, onFilenameChange, onTitleChange, - isEditable, isSelected, + isFilenameEditable, + isTitleEditable, + title, }) => { const styledCardContent = { px: onFilenameChange ? 0 : 1, @@ -48,13 +52,16 @@ export const ThumbnailContent: FC = ({ {onFilenameChange ? ( - + ) => onFilenameChange(e.target.value.replace(" ", "-")) } @@ -79,15 +86,16 @@ export const ThumbnailContent: FC = ({ }, }} /> - + void; onTitleChange?: (value: string) => void; onClick?: () => void; + showRemove?: boolean; + isFilenameEditable?: boolean; + isTitleEditable?: boolean; + title?: string; } export const Thumbnail: FC = ({ src, url, filename, - isEditable, + isDraggable, showVideo, onRemove, onFilenameChange, @@ -83,6 +87,10 @@ export const Thumbnail: FC = ({ onTitleChange, imageHeight, selectable, + showRemove = true, + isFilenameEditable, + isTitleEditable, + title, }) => { const theme = useTheme(); const imageEl = useRef(); @@ -119,6 +127,10 @@ export const Thumbnail: FC = ({ }; const RemoveIcon = () => { + if (!showRemove) { + return <>; + } + return ( <> {onRemove && ( @@ -331,7 +343,7 @@ export const Thumbnail: FC = ({ sx={styledCard} elevation={0} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} data-cy={id} > @@ -408,10 +420,12 @@ export const Thumbnail: FC = ({ file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -425,7 +439,7 @@ export const Thumbnail: FC = ({ sx={styledCard} elevation={0} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} data-cy={id} onDragStart={(evt) => onDragStart(evt)} > @@ -503,8 +517,9 @@ export const Thumbnail: FC = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -516,7 +531,7 @@ export const Thumbnail: FC = ({ sx={styledCard} elevation={0} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -588,7 +604,7 @@ export const Thumbnail: FC = ({ sx={styledCard} elevation={0} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -663,7 +680,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -737,7 +755,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -812,7 +831,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -889,7 +909,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -972,7 +993,7 @@ export const Thumbnail: FC = ({ elevation={0} data-cy={id} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1069,7 +1091,7 @@ export const Thumbnail: FC = ({ elevation={0} data-cy={id} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1147,7 +1170,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1220,7 +1244,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1293,7 +1318,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1366,7 +1392,7 @@ export const Thumbnail: FC = ({ elevation={0} data-cy={id} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1439,7 +1466,7 @@ export const Thumbnail: FC = ({ elevation={0} data-cy={id} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1515,7 +1543,7 @@ export const Thumbnail: FC = ({ elevation={0} data-cy={id} onClick={isSelecting ? handleSelect : onClick} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1595,7 +1624,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1675,7 +1705,7 @@ export const Thumbnail: FC = ({ elevation={0} onClick={isSelecting ? handleSelect : onClick} data-cy={id} - draggable={!isEditable} + draggable={!isDraggable} onDragStart={(evt) => onDragStart(evt)} > = ({ filename={filename} onFilenameChange={onFilenameChange} onTitleChange={onTitleChange} - isEditable={isEditable} isSelected={selectedFiles.some((file) => file.id === id)} + isTitleEditable={isTitleEditable} + isFilenameEditable={isFilenameEditable} /> ); @@ -1759,6 +1790,6 @@ export const Thumbnail: FC = ({ }; Thumbnail.defaultProps = { - isEditable: false, + isDraggable: false, showVideo: true, }; diff --git a/src/apps/media/src/app/components/UploadModal.tsx b/src/apps/media/src/app/components/UploadModal.tsx index d7cee6d083..3c2c4e0eec 100644 --- a/src/apps/media/src/app/components/UploadModal.tsx +++ b/src/apps/media/src/app/components/UploadModal.tsx @@ -31,9 +31,13 @@ import pluralizeWord from "../../../../../utility/pluralizeWord"; export const UploadModal: FC = () => { const dispatch = useDispatch(); - const uploads = useSelector((state: AppState) => state.mediaRevamp.uploads); + const uploads = useSelector((state: AppState) => + state.mediaRevamp.uploads.filter((upload) => !upload.replacementFile) + ); const filesToUpload = useSelector((state: AppState) => - state.mediaRevamp.uploads.filter((upload) => upload.status !== "failed") + state.mediaRevamp.uploads.filter( + (upload) => upload.status !== "failed" && !upload.replacementFile + ) ); const ids = filesToUpload.length && { currentBinId: filesToUpload[0].bin_id, @@ -186,8 +190,14 @@ const UploadErrors = () => { type UploadHeaderTextProps = { uploads: Upload[]; + headerKeyword?: string; + showCount?: boolean; }; -const UploadHeaderText = ({ uploads }: UploadHeaderTextProps) => { +export const UploadHeaderText = ({ + uploads, + headerKeyword = "File", + showCount = true, +}: UploadHeaderTextProps) => { const filesUploading = uploads?.filter( (upload) => upload.status === "inProgress" ); @@ -225,12 +235,18 @@ const UploadHeaderText = ({ uploads }: UploadHeaderTextProps) => { )} + {showCount ? ( + filesUploading?.length > 0 ? ( + filesUploading.length + ) : ( + filesUploaded.length + ) + ) : ( + <> + )}{" "} {filesUploading?.length > 0 - ? filesUploading.length - : filesUploaded.length}{" "} - {filesUploading?.length > 0 - ? pluralizeWord("File", filesUploading.length) - : pluralizeWord("File", filesUploaded.length)}{" "} + ? pluralizeWord(headerKeyword, filesUploading.length) + : pluralizeWord(headerKeyword, filesUploaded.length)}{" "} {filesUploading?.length > 0 ? "Uploading" : "Uploaded"} diff --git a/src/apps/media/src/app/components/UploadThumbnail.tsx b/src/apps/media/src/app/components/UploadThumbnail.tsx index 83504e031f..35d7f0b5c2 100644 --- a/src/apps/media/src/app/components/UploadThumbnail.tsx +++ b/src/apps/media/src/app/components/UploadThumbnail.tsx @@ -13,13 +13,23 @@ import { Upload, fileUploadSetFilename, deleteUpload, + replaceFile, } from "../../../../../shell/store/media-revamp"; +import { File as ZestyMediaFile } from "../../../../../shell/services/types"; interface Props { file: Upload; + action?: "new" | "replace"; + originalFile?: ZestyMediaFile; + showRemove?: boolean; } -export const UploadThumbnail: FC = ({ file }) => { +export const UploadThumbnail: FC = ({ + file, + action = "new", + originalFile, + showRemove = true, +}) => { const dispatch = useDispatch(); const { data: bin } = mediaManagerApi.useGetBinQuery(file.bin_id, { @@ -28,7 +38,11 @@ export const UploadThumbnail: FC = ({ file }) => { useEffect(() => { if (bin && file.status === "staged") { - dispatch(uploadFile(file, bin[0])); + if (action === "new") { + dispatch(uploadFile(file, bin[0])); + } else { + dispatch(replaceFile(file, originalFile)); + } } }, [bin]); @@ -61,10 +75,14 @@ export const UploadThumbnail: FC = ({ file }) => { > { if (file.status === "success") { dispatch( diff --git a/src/shell/components/InviteMembersModal/index.tsx b/src/shell/components/InviteMembersModal/index.tsx index 5c5b5ce2b0..5e6b9a7035 100644 --- a/src/shell/components/InviteMembersModal/index.tsx +++ b/src/shell/components/InviteMembersModal/index.tsx @@ -21,7 +21,7 @@ import { useGetCurrentUserRolesQuery, } from "../../services/accounts"; import { LoadingButton } from "@mui/lab"; -import { NoPermission } from "./NoPermission"; +import { NoPermission } from "../NoPermission"; import instanzeZUID from "../../../utility/instanceZUID"; import { ConfirmationModal } from "./ConfirmationDialog"; diff --git a/src/shell/components/InviteMembersModal/NoPermission.tsx b/src/shell/components/NoPermission.tsx similarity index 80% rename from src/shell/components/InviteMembersModal/NoPermission.tsx rename to src/shell/components/NoPermission.tsx index b22c8d1bc3..9ca0ec879f 100644 --- a/src/shell/components/InviteMembersModal/NoPermission.tsx +++ b/src/shell/components/NoPermission.tsx @@ -15,14 +15,20 @@ import { } from "@mui/material"; import ErrorRoundedIcon from "@mui/icons-material/ErrorRounded"; -import { useGetUsersRolesQuery } from "../../services/accounts"; -import { MD5 } from "../../../utility/md5"; +import { useGetUsersRolesQuery } from "../services/accounts"; +import { MD5 } from "../../utility/md5"; type NoPermissionProps = { onClose: () => void; + headerTitle?: string; + headerSubtitle?: string; }; -export const NoPermission = ({ onClose }: NoPermissionProps) => { +export const NoPermission = ({ + onClose, + headerSubtitle, + headerTitle, +}: NoPermissionProps) => { const { data: users } = useGetUsersRolesQuery(); const ownersAndAdmins = useMemo(() => { @@ -51,12 +57,14 @@ export const NoPermission = ({ onClose }: NoPermissionProps) => { }} /> - You do not have permission to invite users + {headerTitle + ? headerTitle + : "You do not have permission to invite users"} - Contact your instance owners or administrators listed below to change - your role to Admin or Owner on this instance for user invitation - priveleges. + {headerSubtitle + ? headerSubtitle + : "Contact your instance owners or administrators listed below to change your role to Admin or Owner on this instance for user invitation priveleges."} diff --git a/src/shell/services/types.ts b/src/shell/services/types.ts index 1af2f44da7..b8658b0d48 100644 --- a/src/shell/services/types.ts +++ b/src/shell/services/types.ts @@ -60,6 +60,8 @@ export interface File { deleted_at?: string; deleted_from_storage_at?: string; thumbnail: string; + storage_driver: string; + storage_name: string; } export type ModelType = "pageset" | "templateset" | "dataset"; diff --git a/src/shell/services/util.js b/src/shell/services/util.js index a8e232048c..1b3236a582 100644 --- a/src/shell/services/util.js +++ b/src/shell/services/util.js @@ -10,5 +10,13 @@ export const prepareHeaders = (headers) => { return headers; }; -export const generateThumbnail = (file) => - `${file.url}?width=300&height=300&fit=bounds`; +export const generateThumbnail = (file) => { + if (!!file.updated_at && !isNaN(new Date(file.updated_at).getTime())) { + // Prevents browser image cache when a certain file has been already replaced + return `${file.url}?width=300&height=300&fit=bounds&versionHash=${new Date( + file.updated_at + ).getTime()}`; + } + + return `${file.url}?width=300&height=300&fit=bounds`; +}; diff --git a/src/shell/store/media-revamp.ts b/src/shell/store/media-revamp.ts index f75d83af73..239303430b 100644 --- a/src/shell/store/media-revamp.ts +++ b/src/shell/store/media-revamp.ts @@ -27,12 +27,18 @@ export type UploadFile = { loading?: boolean; bin_id?: string; group_id?: string; + replacementFile?: boolean; }; type FileUploadStart = StoreFile & { file: File }; type FileUploadSuccess = StoreFile & FileBase & { id: string }; type FileUploadProgress = { uploadID: string; progress: number }; -type FileUploadStageArg = { file: File; bin_id: string; group_id: string }; +type FileUploadStageArg = { + file: File; + bin_id: string; + group_id: string; + replacementFile?: boolean; +}; type StagedUpload = { status: "staged"; @@ -146,6 +152,7 @@ const mediaSlice = createSlice({ uploadID: uuidv4(), url: URL.createObjectURL(file.file), filename: file.file.name, + replacementFile: file.replacementFile, ...file, }; }); @@ -334,11 +341,11 @@ type FileAugmentation = { group_id?: string; }; -async function getSignedUrl(file: any, bin: Bin) { +async function getSignedUrl(filename: string, storageName: string) { try { return request( //@ts-expect-error - `${CONFIG.SERVICE_MEDIA_STORAGE}/signed-url/${bin.storage_name}/${file.file.name}` + `${CONFIG.SERVICE_MEDIA_STORAGE}/signed-url/${storageName}/${filename}` ).then((res) => res.data.url); } catch (err) { console.error(err); @@ -349,6 +356,152 @@ async function getSignedUrl(file: any, bin: Bin) { } } +export function replaceFile(newFile: UploadFile, originalFile: FileBase) { + return async (dispatch: Dispatch, getState: () => AppState) => { + const bodyData = new FormData(); + const req = new XMLHttpRequest(); + const file = { + progress: 0, + loading: true, + ...newFile, + }; + + bodyData.append("file", file.file, originalFile.filename); + bodyData.append("file_id", originalFile.id); + + req.upload.addEventListener("progress", function (e) { + file.progress = (e.loaded / e.total) * 100; + + dispatch(fileUploadProgress(file)); + }); + + function handleError() { + dispatch(fileUploadError(file)); + dispatch( + notify({ + message: "Failed uploading file", + kind: "error", + }) + ); + } + + req.addEventListener("abort", handleError); + req.addEventListener("error", handleError); + req.addEventListener("load", (_) => { + if (req.status === 200) { + dispatch( + notify({ + message: `File Replaced: ${originalFile.filename}`, + kind: "success", + }) + ); + const successFile = { + ...originalFile, + uploadID: file.uploadID, + progress: 100, + loading: false, + url: URL.createObjectURL(file.file), + }; + dispatch(fileUploadSuccess(successFile)); + } else { + dispatch( + notify({ + message: "Failed uploading file", + kind: "error", + }) + ); + dispatch(fileUploadError(file)); + } + }); + + // Use signed url flow for large files + if (file.file.size > 32000000) { + /** + * GAE has an inherent 32mb limit at their global nginx load balancer + * We use a signed url for large file uploads directly to the assocaited bucket + */ + + const signedUrl = await getSignedUrl( + originalFile?.filename, + originalFile?.storage_name + ); + req.open("PUT", signedUrl); + + // The sent content-type needs to match what was provided when generating the signed url + // @see https://medium.com/imersotechblog/upload-files-to-google-cloud-storage-gcs-from-the-browser-159810bb11e3 + req.setRequestHeader("Content-Type", file.file.type); + + req.addEventListener("load", () => { + if (req.status === 200) { + return request( + //@ts-expect-error + `${CONFIG.SERVICE_MEDIA_MANAGER}/file/${originalFile?.id}/purge?triggerUpdate=true`, + { + method: "POST", + json: true, + } + ) + .then((res) => { + if (res.status === 200) { + const state: State = getState().mediaRevamp; + if (state.uploads.length) { + dispatch( + fileUploadSuccess({ + ...res.data, + uploadID: file.uploadID, + }) + ); + } else { + dispatch( + notify({ + message: `Successfully uploaded file`, + kind: "success", + }) + ); + } + } else { + throw res; + } + }) + .catch((err) => { + dispatch(fileUploadError(file)); + dispatch( + notify({ + message: + "Failed creating file record after signed url upload", + kind: "error", + }) + ); + }); + } else { + dispatch(fileUploadError(file)); + dispatch( + notify({ + message: "Failed uploading file to signed url", + kind: "error", + }) + ); + } + }); + + // When sending directly to bucket it needs to be just the file + // and not the extra meta data for the zesty services + req.send(file.file); + } else { + req.withCredentials = true; + req.open( + "PUT", + //@ts-expect-error + `${CONFIG.SERVICE_MEDIA_STORAGE}/replace/${originalFile?.storage_driver}/${originalFile?.storage_name}` + ); + + req.send(bodyData); + } + + dispatch(fileUploadStart(file)); + }; +} + //type FileMonstrosity = {file: File } & FileAugmentation & FileBase export function uploadFile(fileArg: UploadFile, bin: Bin) { return async (dispatch: Dispatch, getState: () => AppState) => { @@ -417,7 +570,7 @@ export function uploadFile(fileArg: UploadFile, bin: Bin) { * We use a signed url for large file uploads directly to the assocaited bucket */ - const signedUrl = await getSignedUrl(file, bin); + const signedUrl = await getSignedUrl(file.file.name, bin.storage_name); req.open("PUT", signedUrl); // The sent content-type needs to match what was provided when generating the signed url @@ -596,16 +749,18 @@ export function dismissFileUploads() { ); } if (successfulUploads.length) { - dispatch( - notify({ - message: `Successfully uploaded ${successfulUploads.length} files${ - inProgressUploads.length - ? `...${inProgressUploads.length} files still in progress` - : "" - }`, - kind: "success", - }) - ); + if (!successfulUploads[0].replacementFile) { + dispatch( + notify({ + message: `Successfully uploaded ${successfulUploads.length} files${ + inProgressUploads.length + ? `...${inProgressUploads.length} files still in progress` + : "" + }`, + kind: "success", + }) + ); + } } if (failedUploads.length) { dispatch( @@ -622,6 +777,12 @@ export function dismissFileUploads() { kind: "warn", }) ); + } else { + successfulUploads?.forEach((upload) => { + dispatch( + mediaManagerApi.util.invalidateTags([{ type: "File", id: upload.id }]) + ); + }); } dispatch(fileUploadReset()); };