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/package-lock.json b/package-lock.json
index 73d29b28d9..4435c8d046 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,7 +5,6 @@
"requires": true,
"packages": {
"": {
- "name": "manager-ui",
"version": "1.0.1",
"license": "Commons Clause License Condition v1.0",
"dependencies": {
@@ -31,7 +30,7 @@
"@tinymce/tinymce-react": "^4.3.0",
"@welldone-software/why-did-you-render": "^6.1.1",
"@zesty-io/core": "1.10.0",
- "@zesty-io/material": "^0.15.2",
+ "@zesty-io/material": "^0.15.3",
"chart.js": "^3.8.0",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-datalabels": "^2.0.0",
@@ -3912,9 +3911,9 @@
}
},
"node_modules/@zesty-io/material": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@zesty-io/material/-/material-0.15.2.tgz",
- "integrity": "sha512-m5dLNBpZtPtXUlo57In+k2ldo8OtAUPLvjLATV7S4qC6GKVSZHpq4Sqvab3YZ8C2dskcHGTos4aOJicgI3/UKA==",
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/@zesty-io/material/-/material-0.15.3.tgz",
+ "integrity": "sha512-zJOagsYexWOq1Bw+L0X1nSKTL+aZkB7MZPCqW53uqAwnKMZ3mejpsOjJwvzoYtm+6oJ6RHQJu0xmNbPXCcjLKA==",
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
@@ -18397,9 +18396,9 @@
}
},
"@zesty-io/material": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@zesty-io/material/-/material-0.15.2.tgz",
- "integrity": "sha512-m5dLNBpZtPtXUlo57In+k2ldo8OtAUPLvjLATV7S4qC6GKVSZHpq4Sqvab3YZ8C2dskcHGTos4aOJicgI3/UKA==",
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/@zesty-io/material/-/material-0.15.3.tgz",
+ "integrity": "sha512-zJOagsYexWOq1Bw+L0X1nSKTL+aZkB7MZPCqW53uqAwnKMZ3mejpsOjJwvzoYtm+6oJ6RHQJu0xmNbPXCcjLKA==",
"requires": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
diff --git a/package.json b/package.json
index 8ec0d7b68e..0a1397272c 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,7 @@
"@tinymce/tinymce-react": "^4.3.0",
"@welldone-software/why-did-you-render": "^6.1.1",
"@zesty-io/core": "1.10.0",
- "@zesty-io/material": "^0.15.2",
+ "@zesty-io/material": "^0.15.3",
"chart.js": "^3.8.0",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-datalabels": "^2.0.0",
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 @@
+
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/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 (
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/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 (
+
+
+ {liveDomain && (
+
+ )}
+
+ );
+};
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 (
!!error)}
/>
diff --git a/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx b/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx
index 6ea4f8af00..713b146799 100644
--- a/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx
+++ b/src/apps/content-editor/src/app/components/FieldTypeMedia.tsx
@@ -26,7 +26,7 @@ import {
} from "@mui/icons-material";
import { alpha } from "@mui/material/styles";
import { CompactView, Modal, Login } from "@bynder/compact-view";
-import { Bynder } from "@zesty-io/material";
+import { Bynder, FileReplace } from "@zesty-io/material";
import {
useGetBinsQuery,
@@ -43,6 +43,8 @@ import styles from "../../../../media/src/app/components/Thumbnail/Loading.less"
import cx from "classnames";
import { FileTypePreview } from "../../../../media/src/app/components/FileModal/FileTypePreview";
import { useGetInstanceSettingsQuery } from "../../../../../shell/services/instance";
+import { ReplaceFileModal } from "../../../../media/src/app/components/FileModal/ReplaceFileModal";
+import { showReportDialog } from "@sentry/react";
type FieldTypeMediaProps = {
images: string[];
@@ -53,6 +55,7 @@ type FieldTypeMediaProps = {
hasError?: boolean;
hideDrag?: boolean;
lockedToGroupId: string | null;
+ settings?: any;
};
export const FieldTypeMedia = ({
@@ -64,6 +67,7 @@ export const FieldTypeMedia = ({
hasError,
hideDrag,
lockedToGroupId,
+ settings,
}: FieldTypeMediaProps) => {
const [draggedIndex, setDraggedIndex] = useState(null);
const [hoveredIndex, setHoveredIndex] = useState(null);
@@ -77,6 +81,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 +114,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 +217,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 +239,7 @@ export const FieldTypeMedia = ({
return zuid;
});
+
onChange(newImageZUIDs.join(","), name);
};
@@ -160,6 +247,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 +423,11 @@ export const FieldTypeMedia = ({
)}
+ {selectionError && (
+
+ {selectionError}
+
+ )}
setIsBynderOpen(false)}>
@@ -421,6 +526,11 @@ export const FieldTypeMedia = ({
)}
+ {selectionError && (
+
+ {selectionError}
+
+ )}
{showFileModal && (
)}
@@ -686,7 +798,7 @@ const MediaItem = ({
)}
{!isBynderAsset || (isBynderAsset && isBynderSessionValid) ? (
-
+
{
@@ -753,6 +865,18 @@ const MediaItem = ({
Rename
)}
+
{!isURL && !isBynderAsset && (
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
-
+
+
{type !== "dataset" && (
-
+
+
)}
-
+
+
{!isDataset && (
-
+
+
)}
-
-
-
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..75d836eea0 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 {
+ ChevronRightOutlined,
+ KeyboardArrowDownRounded,
+} 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]) => (
))}
+
+ More
+
+ >
+ }
+ PaperProps={{
+ sx: {
+ width: 240,
+ },
+ }}
+ >
+
+
+
+
+
{
?.map((field) => (
);
diff --git a/src/apps/media/src/app/components/FileModal/FileTypePreview.tsx b/src/apps/media/src/app/components/FileModal/FileTypePreview.tsx
index 66c22b5b67..7de1e7b234 100644
--- a/src/apps/media/src/app/components/FileModal/FileTypePreview.tsx
+++ b/src/apps/media/src/app/components/FileModal/FileTypePreview.tsx
@@ -32,6 +32,7 @@ import ReportGmailerrorredIcon from "@mui/icons-material/ReportGmailerrorred";
interface Props {
src: string;
filename: string;
+ updatedAt?: string;
imageSettings?: any;
isMediaThumbnail?: boolean;
}
@@ -41,6 +42,7 @@ export const FileTypePreview: FC = ({
filename,
imageSettings,
isMediaThumbnail,
+ updatedAt,
}) => {
const theme = useTheme();
const isLargeScreen = useMediaQuery(theme.breakpoints.up("lg"));
@@ -86,6 +88,11 @@ export const FileTypePreview: FC = ({
const defaultImageSettings = {
width: 800,
optimize: "high",
+ // Prevents browser image cache when a certain file has been already replaced
+ ...(!!updatedAt &&
+ !isNaN(new Date(updatedAt).getTime()) && {
+ versionHash: new Date(updatedAt).getTime(),
+ }),
};
if (isLargeScreen) {
@@ -377,7 +384,8 @@ export const FileTypePreview: FC = ({
void;
+ onCancel: () => void;
+};
+export const ReplaceFileModal = ({
+ onClose,
+ originalFile,
+ onCancel,
+}: ReplaceFileModalProps) => {
+ const dispatch = useDispatch();
+ const [newFile, setNewFile] = useState(null);
+ const [showUploadingFileModal, setShowUploadingFileModal] = useState(false);
+ const hiddenFileInput = useRef(null);
+ const { data: currentUserRoles } = useGetCurrentUserRolesQuery();
+ const uploads = useSelector((state: AppState) => state.mediaRevamp.uploads);
+ const filesToUpload = uploads.filter((upload) => upload.status !== "failed");
+
+ const canReplaceImage = currentUserRoles
+ ?.filter((role) => role.entityZUID === instanceZUID)
+ ?.some((role) => ["admin", "owner"].includes(role.name?.toLowerCase()));
+
+ const acceptedExtension =
+ fileExtension(originalFile?.url) === "jpg" ||
+ fileExtension(originalFile?.url) === "jpeg"
+ ? ".jpg, .jpeg"
+ : `.${fileExtension(originalFile?.url)}`;
+
+ useEffect(() => {
+ if (newFile) {
+ dispatch(
+ fileUploadStage([
+ {
+ file: newFile,
+ bin_id: originalFile.bin_id,
+ group_id: originalFile.group_id,
+ replacementFile: true,
+ },
+ ])
+ );
+ setShowUploadingFileModal(true);
+ setNewFile(null);
+ }
+ }, [newFile]);
+
+ const handleCloseUploadingFileModal = () => {
+ dispatch(dismissFileUploads());
+
+ if (filesToUpload.length) {
+ dispatch(
+ mediaManagerApi.util.invalidateTags([
+ { type: "GroupData", id: originalFile.group_id },
+ "BinFiles",
+ ])
+ );
+ }
+
+ onClose();
+ };
+
+ if (!canReplaceImage) {
+ return (
+
+ );
+ }
+
+ if (showUploadingFileModal) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+ {
+ 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 ? (
-
- ) : 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/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 a41e038880..7517f00c6d 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);
@@ -49,7 +54,10 @@ export type FieldNames =
| "regexRestrictPattern"
| "regexRestrictErrorMessage"
| "minValue"
- | "maxValue";
+ | "maxValue"
+ | "currency"
+ | "fileExtensions"
+ | "fileExtensionsErrorMessage";
type FieldType =
| "input"
| "checkbox"
@@ -78,7 +86,14 @@ export interface DropdownOptions {
label: string;
value: string;
}
-interface FieldFormInputProps {
+export type AutocompleteConfig = {
+ inputProps?:
+ | Partial
+ | Partial
+ | Partial;
+ maxHeight?: number;
+};
+type FieldFormInputProps = {
fieldConfig: InputField;
errorMsg?: string | [string, string][];
onDataChange: ({
@@ -89,9 +104,13 @@ interface FieldFormInputProps {
value: FormValue;
}) => void;
prefillData?: FormValue;
- dropdownOptions?: DropdownOptions[];
+ dropdownOptions?: DropdownOptions[] | Currency[];
disabled?: boolean;
-}
+ autocompleteConfig?: AutocompleteConfig;
+} & Pick<
+ AutocompleteProps,
+ "renderOption" | "filterOptions"
+>;
export const FieldFormInput = ({
fieldConfig,
errorMsg,
@@ -99,6 +118,9 @@ export const FieldFormInput = ({
prefillData,
dropdownOptions,
disabled,
+ renderOption,
+ filterOptions,
+ autocompleteConfig,
}: FieldFormInputProps) => {
const options =
fieldConfig.type === "options" ||
@@ -212,9 +234,19 @@ export const FieldFormInput = ({
{fieldConfig.type === "autocomplete" && (
<>
-
- {fieldConfig.label}
-
+
+
+ {fieldConfig.label}
+
+ {fieldConfig.tooltip && (
+
+
+
+ )}
+
)}
isOptionEqualToValue={(option, value) =>
@@ -246,16 +284,22 @@ export const FieldFormInput = ({
height: "40px",
},
}}
+ renderOption={renderOption}
+ filterOptions={filterOptions}
+ slotProps={{
+ paper: {
+ sx: {
+ "& .MuiAutocomplete-listbox": {
+ maxHeight: autocompleteConfig?.maxHeight || "40vh",
+ boxSizing: "border-box",
+ },
+ },
+ },
+ }}
/>
{prefillData &&
!dropdownOptions.find((option) => option.value === prefillData) && (
-
+
{fieldConfig.name === "group_id" &&
"The folder this was locked to has been deleted"}
{fieldConfig.name === "relatedModelZUID" &&
@@ -324,12 +368,9 @@ export const FieldFormInput = ({
error={Boolean(errorMsg)}
helperText={
errorMsg && (
-
+
{errorMsg}
-
+
)
}
type={fieldConfig.inputType || "text"}
@@ -445,9 +486,9 @@ const KeyValueInput = ({
handleDataChanged("value", e.target?.value);
}}
helperText={
-
+
{labelErrorMsg}
-
+
}
error={Boolean(labelErrorMsg)}
disabled={disabledFields.includes("value")}
@@ -463,9 +504,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/MediaRules.tsx b/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx
index 18223ec1a1..6660a1c11d 100644
--- a/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx
+++ b/src/apps/schema/src/app/components/AddFieldModal/MediaRules.tsx
@@ -4,14 +4,22 @@ import {
Checkbox,
Typography,
Stack,
+ InputLabel,
+ Autocomplete,
+ TextField,
+ Chip,
} from "@mui/material";
-import { FormValue } from "./views/FieldForm";
+import { Errors, FormValue } from "./views/FieldForm";
import { CustomGroup } from "../hooks/useMediaRules";
-
import { InputField } from "./FieldFormInput";
import { FieldFormInput, FieldNames } from "./FieldFormInput";
+import { useEffect, useState } from "react";
+
+type MediaFieldName = Extract<
+ FieldNames,
+ "limit" | "group_id" | "fileExtensions"
+>;
-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,90 @@ 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.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 === " ") &&
+ inputValue
+ ) {
+ 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("");
+ }
+ } 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.trim() !== option.trim()
+ );
+ 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 +274,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..661edcf3ff 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";
@@ -27,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 {
@@ -64,6 +73,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 +237,12 @@ 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") {
+ formFields[field.name] = fieldData.settings[field.name] ?? null;
} else {
formFields[field.name] = fieldData[field.name] as FormValue;
}
@@ -249,7 +269,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 +415,26 @@ export const FieldForm = ({
}
}
+ if (inputName === "currency" && !formData.currency) {
+ newErrorsObj[inputName] = "Please select a currency";
+ }
+
+ 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 +447,9 @@ export const FieldForm = ({
"regexRestrictErrorMessage",
"minValue",
"maxValue",
+ "currency",
+ "fileExtensions",
+ "fileExtensionsErrorMessage",
].includes(inputName)
) {
const { maxLength, label, validate } = FORM_CONFIG[type].details.find(
@@ -508,7 +553,9 @@ export const FieldForm = ({
errors.regexRestrictPattern ||
errors.regexRestrictErrorMessage ||
errors.minValue ||
- errors.maxValue
+ errors.maxValue ||
+ errors.fileExtensions ||
+ errors.fileExtensionsErrorMessage
) {
setActiveTab("rules");
} else {
@@ -562,6 +609,16 @@ 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[],
+ }),
+ ...(formData.fileExtensionsErrorMessage && {
+ fileExtensionsErrorMessage:
+ formData.fileExtensionsErrorMessage as string,
+ }),
},
sort: isUpdateField ? fieldData.sort : sort, // Just use the length since sort starts at 0
};
@@ -757,6 +814,9 @@ export const FieldForm = ({
let dropdownOptions: DropdownOptions[];
let disabled = false;
+ let renderOption: any;
+ let filterOptions: any;
+ let autocompleteConfig: AutocompleteConfig = {};
if (fieldConfig.name === "relatedModelZUID") {
dropdownOptions = modelsOptions;
@@ -768,6 +828,74 @@ 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;
+ }
+ };
+ autocompleteConfig.inputProps = {
+ startAdornment: !!selectedValue && (
+
+
+
+ {selectedValue.value} {selectedValue.symbol_native}
+
+
+ ),
+ };
+ autocompleteConfig.maxHeight = 256;
+ }
+
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 8c7d473707..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,18 +48,6 @@ export const Rules = ({
return (
- {type === "images" && (
-
- )}
-
+ {type === "images" && (
+
+ )}
+
{(type === "text" || type === "textarea") && (
<>
= {
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: {
@@ -556,6 +573,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/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}
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 62%
rename from src/shell/components/FieldTypeCurrency/currencies.js
rename to src/shell/components/FieldTypeCurrency/currencies.ts
index 10102d6fa9..ab439b5265 100644
--- a/src/shell/components/FieldTypeCurrency/currencies.js
+++ b/src/shell/components/FieldTypeCurrency/currencies.ts
@@ -1,1064 +1,1193 @@
-export const currencies = {
- USD: {
- symbol: " $ ",
- name: "US Dollar",
- symbol_native: "$",
- decimal_digits: 2,
- rounding: 0,
- code: "USD",
- name_plural: "US dollars",
- },
- CAD: {
- symbol: "CA$ ",
- name: "Canadian Dollar",
- symbol_native: "$",
- decimal_digits: 2,
- rounding: 0,
- code: "CAD",
- name_plural: "Canadian dollars",
- },
- EUR: {
- symbol: " € ",
- name: "Euro",
- symbol_native: "€",
- decimal_digits: 2,
- rounding: 0,
- code: "EUR",
- name_plural: "euros",
- },
- AED: {
- symbol: "AED",
- name: "United Arab Emirates Dirham",
- symbol_native: "د.إ.",
- decimal_digits: 2,
- rounding: 0,
- code: "AED",
- name_plural: "UAE dirhams",
- },
- AFN: {
+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: "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",
- symbol_native: "դր.",
- decimal_digits: 0,
+ {
+ symbol: "DA",
+ label: "Algerian Dinar",
+ symbol_native: "د.ج.",
+ decimal_digits: 2,
rounding: 0,
- code: "AMD",
- name_plural: "Armenian drams",
+ value: "DZD",
+ name_plural: "Algerian dinars",
+ countryCode: "DZ",
},
- 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: "AMD",
+ label: "Armenian Dram",
+ symbol_native: "դր.",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "AMD",
+ name_plural: "Armenian drams",
+ countryCode: "AM",
+ },
+ {
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",
- symbol_native: "KM",
- decimal_digits: 2,
+ {
+ symbol: "BD",
+ label: "Bahraini Dinar",
+ symbol_native: "د.ب.",
+ decimal_digits: 3,
rounding: 0,
- code: "BAM",
- name_plural: "Bosnia-Herzegovina convertible marks",
+ value: "BHD",
+ name_plural: "Bahraini dinars",
+ countryCode: "BH",
},
- 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",
- symbol_native: "лв.",
- decimal_digits: 2,
- rounding: 0,
- code: "BGN",
- name_plural: "Bulgarian leva",
- },
- BHD: {
- symbol: "BD",
- name: "Bahraini Dinar",
- symbol_native: "د.ب.",
- decimal_digits: 3,
- rounding: 0,
- code: "BHD",
- name_plural: "Bahraini dinars",
- },
- BIF: {
- symbol: "FBu",
- name: "Burundian Franc",
- symbol_native: "FBu",
+ {
+ symbol: "BYR",
+ label: "Belarusian Ruble",
+ symbol_native: "BYR",
decimal_digits: 0,
rounding: 0,
- code: "BIF",
- name_plural: "Burundian francs",
+ value: "BYR",
+ name_plural: "Belarusian rubles",
+ countryCode: "BY",
},
- BND: {
- symbol: "BN$",
- name: "Brunei Dollar",
+ {
+ symbol: "BZ$",
+ label: "Belize Dollar",
symbol_native: "$",
decimal_digits: 2,
rounding: 0,
- code: "BND",
- name_plural: "Brunei dollars",
+ value: "BZD",
+ name_plural: "Belize dollars",
+ countryCode: "BZ",
},
- 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",
- symbol_native: "R$",
+ {
+ symbol: "KM",
+ label: "Bosnia-Herzegovina Convertible Mark",
+ symbol_native: "KM",
decimal_digits: 2,
rounding: 0,
- code: "BRL",
- name_plural: "Brazilian reals",
+ value: "BAM",
+ name_plural: "Bosnia-Herzegovina convertible marks",
+ countryCode: "BA",
},
- 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",
- symbol_native: "BYR",
- decimal_digits: 0,
+ {
+ symbol: "R$",
+ label: "Brazilian Real",
+ symbol_native: "R$",
+ decimal_digits: 2,
rounding: 0,
- code: "BYR",
- name_plural: "Belarusian rubles",
+ value: "BRL",
+ name_plural: "Brazilian reals",
+ countryCode: "BR",
},
- BZD: {
- symbol: "BZ$",
- name: "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,
- code: "BZD",
- name_plural: "Belize dollars",
+ value: "BND",
+ name_plural: "Brunei dollars",
+ countryCode: "BN",
},
- CDF: {
- symbol: "CDF",
- name: "Congolese Franc",
- symbol_native: "FrCD",
+ {
+ symbol: "BGN",
+ label: "Bulgarian Lev",
+ symbol_native: "лв.",
decimal_digits: 2,
rounding: 0,
- code: "CDF",
- name_plural: "Congolese francs",
+ value: "BGN",
+ name_plural: "Bulgarian leva",
+ countryCode: "BG",
},
- CHF: {
- symbol: "CHF",
- name: "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,
- code: "CHF",
- name_plural: "Swiss francs",
+ 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",
},
- CLP: {
+ {
+ 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$",
- 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",
+ },
+ {
+ symbol: "CF",
+ label: "Comorian Franc",
+ symbol_native: "FC",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "KMF",
+ name_plural: "Comorian francs",
+ countryCode: "KM",
},
- CRC: {
+ {
+ symbol: "CDF",
+ label: "Congolese Franc",
+ symbol_native: "FrCD",
+ decimal_digits: 2,
+ rounding: 0,
+ value: "CDF",
+ name_plural: "Congolese francs",
+ countryCode: "CD",
+ },
+ {
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",
- symbol_native: "CV$",
+ {
+ symbol: "kn",
+ label: "Croatian Kuna",
+ symbol_native: "kn",
decimal_digits: 2,
rounding: 0,
- code: "CVE",
- name_plural: "Cape Verdean escudos",
+ value: "HRK",
+ name_plural: "Croatian kunas",
+ countryCode: "HR",
},
- 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",
- symbol_native: "Fdj",
- decimal_digits: 0,
- rounding: 0,
- code: "DJF",
- name_plural: "Djiboutian francs",
- },
- 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: "Fdj",
+ label: "Djiboutian Franc",
+ symbol_native: "Fdj",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "DJF",
+ name_plural: "Djiboutian francs",
+ countryCode: "DJ",
+ },
+ {
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",
- symbol_native: "د.ج.",
- decimal_digits: 2,
- rounding: 0,
- code: "DZD",
- name_plural: "Algerian dinars",
- },
- EEK: {
- symbol: "Ekr",
- name: "Estonian Kroon",
- symbol_native: "kr",
- decimal_digits: 2,
- rounding: 0,
- code: "EEK",
- name_plural: "Estonian kroons",
- },
- 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: "Ekr",
+ label: "Estonian Kroon",
+ symbol_native: "kr",
+ decimal_digits: 2,
+ rounding: 0,
+ value: "EEK",
+ name_plural: "Estonian kroons",
+ countryCode: "EE",
+ },
+ {
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",
- symbol_native: "£",
+ {
+ symbol: " € ",
+ label: "Euro",
+ symbol_native: "€",
decimal_digits: 2,
rounding: 0,
- code: "GBP",
- name_plural: "British pounds sterling",
+ value: "EUR",
+ name_plural: "euros",
+ countryCode: "EU",
},
- 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",
- symbol_native: "FG",
- decimal_digits: 0,
- rounding: 0,
- code: "GNF",
- name_plural: "Guinean francs",
- },
- 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",
- symbol_native: "$",
- decimal_digits: 2,
+ {
+ symbol: "FG",
+ label: "Guinean Franc",
+ symbol_native: "FG",
+ decimal_digits: 0,
rounding: 0,
- code: "HKD",
- name_plural: "Hong Kong dollars",
+ value: "GNF",
+ name_plural: "Guinean francs",
+ countryCode: "GN",
},
- 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",
- symbol_native: "kn",
+ {
+ symbol: "HK$",
+ label: "Hong Kong Dollar",
+ symbol_native: "$",
decimal_digits: 2,
rounding: 0,
- code: "HRK",
- name_plural: "Croatian kunas",
+ value: "HKD",
+ name_plural: "Hong Kong dollars",
+ countryCode: "HK",
},
- 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",
- symbol_native: "Rp",
+ {
+ symbol: "Ikr",
+ label: "Icelandic Króna",
+ symbol_native: "kr",
decimal_digits: 0,
rounding: 0,
- code: "IDR",
- name_plural: "Indonesian rupiahs",
- },
- ILS: {
- symbol: "₪",
- name: "Israeli New Sheqel",
- symbol_native: "₪",
- decimal_digits: 2,
- rounding: 0,
- code: "ILS",
- name_plural: "Israeli new sheqels",
+ value: "ISK",
+ name_plural: "Icelandic krónur",
+ countryCode: "IS",
},
- 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",
- symbol_native: "د.ع.",
+ {
+ symbol: "Rp",
+ label: "Indonesian Rupiah",
+ symbol_native: "Rp",
decimal_digits: 0,
rounding: 0,
- code: "IQD",
- name_plural: "Iraqi dinars",
+ value: "IDR",
+ name_plural: "Indonesian rupiahs",
+ countryCode: "ID",
},
- 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",
- symbol_native: "kr",
+ {
+ symbol: "IQD",
+ label: "Iraqi Dinar",
+ symbol_native: "د.ع.",
decimal_digits: 0,
rounding: 0,
- code: "ISK",
- name_plural: "Icelandic krónur",
+ 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",
},
- 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",
- symbol_native: "د.أ.",
- decimal_digits: 3,
- rounding: 0,
- code: "JOD",
- name_plural: "Jordanian dinars",
- },
- 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",
- symbol_native: "Ksh",
- decimal_digits: 2,
- rounding: 0,
- code: "KES",
- name_plural: "Kenyan shillings",
- },
- KHR: {
- symbol: "KHR",
- name: "Cambodian Riel",
- symbol_native: "៛",
- decimal_digits: 2,
- rounding: 0,
- code: "KHR",
- name_plural: "Cambodian riels",
- },
- KMF: {
- symbol: "CF",
- name: "Comorian Franc",
- symbol_native: "FC",
- decimal_digits: 0,
- rounding: 0,
- code: "KMF",
- name_plural: "Comorian francs",
- },
- KRW: {
- symbol: "₩",
- name: "South Korean Won",
- symbol_native: "₩",
- decimal_digits: 0,
- rounding: 0,
- code: "KRW",
- name_plural: "South Korean won",
- },
- KWD: {
- symbol: "KD",
- name: "Kuwaiti Dinar",
- symbol_native: "د.ك.",
+ {
+ symbol: "JD",
+ label: "Jordanian Dinar",
+ symbol_native: "د.أ.",
decimal_digits: 3,
rounding: 0,
- code: "KWD",
- name_plural: "Kuwaiti dinars",
+ value: "JOD",
+ name_plural: "Jordanian dinars",
+ countryCode: "JO",
},
- KZT: {
+ {
symbol: "KZT",
- name: "Kazakhstani Tenge",
+ label: "Kazakhstani Tenge",
symbol_native: "тңг.",
- decimal_digits: 2,
- rounding: 0,
- code: "KZT",
- name_plural: "Kazakhstani tenges",
- },
- LBP: {
- symbol: "LB£",
- name: "Lebanese Pound",
- symbol_native: "ل.ل.",
- decimal_digits: 0,
+ decimal_digits: 2,
rounding: 0,
- code: "LBP",
- name_plural: "Lebanese pounds",
+ value: "KZT",
+ name_plural: "Kazakhstani tenges",
+ countryCode: "KZ",
},
- LKR: {
- symbol: "SLRs",
- name: "Sri Lankan Rupee",
- symbol_native: "SL Re",
+ {
+ symbol: "Ksh",
+ label: "Kenyan Shilling",
+ symbol_native: "Ksh",
decimal_digits: 2,
rounding: 0,
- code: "LKR",
- name_plural: "Sri Lankan rupees",
+ value: "KES",
+ name_plural: "Kenyan shillings",
+ countryCode: "KE",
},
- LTL: {
- symbol: "Lt",
- name: "Lithuanian Litas",
- symbol_native: "Lt",
- decimal_digits: 2,
+ {
+ symbol: "KD",
+ label: "Kuwaiti Dinar",
+ symbol_native: "د.ك.",
+ decimal_digits: 3,
rounding: 0,
- code: "LTL",
- name_plural: "Lithuanian litai",
+ value: "KWD",
+ name_plural: "Kuwaiti dinars",
+ countryCode: "KW",
},
- 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",
+ },
+ {
+ symbol: "LB£",
+ label: "Lebanese Pound",
+ symbol_native: "ل.ل.",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "LBP",
+ name_plural: "Lebanese pounds",
+ countryCode: "LB",
},
- 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",
- symbol_native: "د.م.",
+ {
+ symbol: "Lt",
+ label: "Lithuanian Litas",
+ symbol_native: "Lt",
decimal_digits: 2,
rounding: 0,
- code: "MAD",
- name_plural: "Moroccan dirhams",
+ value: "LTL",
+ name_plural: "Lithuanian litai",
+ countryCode: "LT",
},
- MDL: {
- symbol: "MDL",
- name: "Moldovan Leu",
- symbol_native: "MDL",
+ {
+ symbol: "MOP$",
+ label: "Macanese Pataca",
+ symbol_native: "MOP$",
decimal_digits: 2,
rounding: 0,
- code: "MDL",
- name_plural: "Moldovan lei",
- },
- MGA: {
- symbol: "MGA",
- name: "Malagasy Ariary",
- symbol_native: "MGA",
- decimal_digits: 0,
- rounding: 0,
- code: "MGA",
- name_plural: "Malagasy Ariaries",
+ value: "MOP",
+ name_plural: "Macanese patacas",
+ countryCode: "MO",
},
- 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",
- symbol_native: "K",
+ {
+ symbol: "MGA",
+ label: "Malagasy Ariary",
+ symbol_native: "MGA",
decimal_digits: 0,
rounding: 0,
- code: "MMK",
- name_plural: "Myanma kyats",
+ value: "MGA",
+ name_plural: "Malagasy Ariaries",
+ countryCode: "MG",
},
- MOP: {
- symbol: "MOP$",
- name: "Macanese Pataca",
- symbol_native: "MOP$",
+ {
+ symbol: "RM",
+ label: "Malaysian Ringgit",
+ symbol_native: "RM",
decimal_digits: 2,
rounding: 0,
- code: "MOP",
- name_plural: "Macanese patacas",
+ value: "MYR",
+ name_plural: "Malaysian ringgits",
+ countryCode: "MY",
},
- 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",
- symbol_native: "RM",
+ {
+ symbol: "MDL",
+ label: "Moldovan Leu",
+ symbol_native: "MDL",
decimal_digits: 2,
rounding: 0,
- code: "MYR",
- name_plural: "Malaysian ringgits",
+ 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",
},
- 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",
+ },
+ {
+ symbol: "MMK",
+ label: "Myanma Kyat",
+ symbol_native: "K",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "MMK",
+ name_plural: "Myanma kyats",
+ countryCode: "MM",
},
- 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",
- symbol_native: "₦",
+ {
+ symbol: "NPRs",
+ label: "Nepalese Rupee",
+ symbol_native: "नेरू",
decimal_digits: 2,
rounding: 0,
- code: "NGN",
- name_plural: "Nigerian nairas",
+ value: "NPR",
+ name_plural: "Nepalese rupees",
+ countryCode: "NP",
},
- NIO: {
- symbol: "C$",
- name: "Nicaraguan Córdoba",
- symbol_native: "C$",
+ {
+ symbol: "NT$",
+ label: "New Taiwan Dollar",
+ symbol_native: "NT$",
decimal_digits: 2,
rounding: 0,
- code: "NIO",
- name_plural: "Nicaraguan córdobas",
+ value: "TWD",
+ name_plural: "New Taiwan dollars",
+ countryCode: "TW",
},
- NOK: {
- symbol: "Nkr",
- name: "Norwegian Krone",
- symbol_native: "kr",
+ {
+ symbol: "NZ$",
+ label: "New Zealand Dollar",
+ symbol_native: "$",
decimal_digits: 2,
rounding: 0,
- code: "NOK",
- name_plural: "Norwegian kroner",
+ value: "NZD",
+ name_plural: "New Zealand dollars",
+ countryCode: "NZ",
},
- NPR: {
- symbol: "NPRs",
- name: "Nepalese Rupee",
- symbol_native: "नेरू",
+ {
+ symbol: "C$",
+ label: "Nicaraguan Córdoba",
+ symbol_native: "C$",
decimal_digits: 2,
rounding: 0,
- code: "NPR",
- name_plural: "Nepalese rupees",
+ value: "NIO",
+ name_plural: "Nicaraguan córdobas",
+ countryCode: "NI",
},
- NZD: {
- symbol: "NZ$",
- name: "New Zealand Dollar",
- symbol_native: "$",
+ {
+ symbol: "₦",
+ label: "Nigerian Naira",
+ symbol_native: "₦",
decimal_digits: 2,
rounding: 0,
- code: "NZD",
- name_plural: "New Zealand dollars",
+ value: "NGN",
+ name_plural: "Nigerian nairas",
+ countryCode: "NG",
+ },
+ {
+ symbol: "Nkr",
+ label: "Norwegian Krone",
+ symbol_native: "kr",
+ decimal_digits: 2,
+ rounding: 0,
+ value: "NOK",
+ name_plural: "Norwegian kroner",
+ countryCode: "NO",
},
- 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",
+ },
+ {
+ symbol: "PKRs",
+ label: "Pakistani Rupee",
+ symbol_native: "₨",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "PKR",
+ name_plural: "Pakistani rupees",
+ countryCode: "PK",
},
- 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",
+ },
+ {
+ symbol: "₲",
+ label: "Paraguayan Guarani",
+ symbol_native: "₲",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "PYG",
+ name_plural: "Paraguayan guaranis",
+ countryCode: "PY",
},
- 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",
- symbol_native: "₨",
- decimal_digits: 0,
- rounding: 0,
- code: "PKR",
- name_plural: "Pakistani rupees",
- },
- 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",
- symbol_native: "₲",
- decimal_digits: 0,
- rounding: 0,
- code: "PYG",
- name_plural: "Paraguayan guaranis",
- },
- 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",
- symbol_native: "дин.",
- decimal_digits: 0,
- rounding: 0,
- code: "RSD",
- name_plural: "Serbian dinars",
- },
- 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",
- symbol_native: "SDG",
- decimal_digits: 2,
- rounding: 0,
- code: "SDG",
- name_plural: "Sudanese pounds",
- },
- SEK: {
- symbol: "Skr",
- name: "Swedish Krona",
- symbol_native: "kr",
- decimal_digits: 2,
+ {
+ symbol: "din.",
+ label: "Serbian Dinar",
+ symbol_native: "дин.",
+ decimal_digits: 0,
rounding: 0,
- code: "SEK",
- name_plural: "Swedish kronor",
+ value: "RSD",
+ name_plural: "Serbian dinars",
+ countryCode: "RS",
},
- 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",
+ },
+ {
+ 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",
},
- 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",
+ },
+ {
+ symbol: "TSh",
+ label: "Tanzanian Shilling",
+ symbol_native: "TSh",
+ decimal_digits: 0,
+ rounding: 0,
+ value: "TZS",
+ name_plural: "Tanzanian shillings",
+ countryCode: "TZ",
},
- 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",
- symbol_native: "د.ت.",
- decimal_digits: 3,
- rounding: 0,
- code: "TND",
- name_plural: "Tunisian dinars",
- },
- 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",
- symbol_native: "TL",
- decimal_digits: 2,
- rounding: 0,
- code: "TRY",
- name_plural: "Turkish Lira",
- },
- 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",
- 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,
- code: "TWD",
- name_plural: "New Taiwan dollars",
+ value: "TRY",
+ name_plural: "Turkish Lira",
+ countryCode: "TR",
},
- TZS: {
- symbol: "TSh",
- name: "Tanzanian Shilling",
- symbol_native: "TSh",
+ {
+ symbol: "USh",
+ label: "Ugandan Shilling",
+ symbol_native: "USh",
decimal_digits: 0,
rounding: 0,
- code: "TZS",
- name_plural: "Tanzanian shillings",
+ value: "UGX",
+ name_plural: "Ugandan shillings",
+ countryCode: "UG",
},
- 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",
- symbol_native: "USh",
- decimal_digits: 0,
+ {
+ symbol: "AED",
+ label: "United Arab Emirates Dirham",
+ symbol_native: "د.إ.",
+ decimal_digits: 2,
rounding: 0,
- code: "UGX",
- name_plural: "Ugandan shillings",
+ 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",
},
- 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",
- symbol_native: "FCFA",
- decimal_digits: 0,
- rounding: 0,
- code: "XAF",
- name_plural: "CFA francs BEAC",
- },
- XOF: {
- symbol: "CFA",
- name: "CFA Franc BCEAO",
- symbol_native: "CFA",
- decimal_digits: 0,
- rounding: 0,
- code: "XOF",
- name_plural: "CFA francs BCEAO",
- },
- 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",
- symbol_native: "R",
- decimal_digits: 2,
- rounding: 0,
- code: "ZAR",
- name_plural: "South African rand",
- },
- 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/Filters/FilterButton.tsx b/src/shell/components/Filters/FilterButton.tsx
index 04f3874e6f..917d7d5e1e 100644
--- a/src/shell/components/Filters/FilterButton.tsx
+++ b/src/shell/components/Filters/FilterButton.tsx
@@ -1,6 +1,6 @@
import { FC } from "react";
import { Button, ButtonGroup, Typography } from "@mui/material";
-import ArrowDropDownOutlinedIcon from "@mui/icons-material/ArrowDropDownOutlined";
+import KeyboardArrowDownRoundedIcon from "@mui/icons-material/KeyboardArrowDownRounded";
import CheckIcon from "@mui/icons-material/Check";
import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
@@ -53,7 +53,7 @@ export const FilterButton: FC = ({
variant="outlined"
size="small"
color="inherit"
- endIcon={}
+ endIcon={}
onClick={onOpenMenu}
data-cy={`${filterId}_default`}
sx={{
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/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/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,
},
];
}
diff --git a/src/shell/services/types.ts b/src/shell/services/types.ts
index 62c7cd1aac..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";
@@ -203,12 +205,16 @@ export interface FieldSettings {
regexRestrictErrorMessage?: string;
minValue?: number;
maxValue?: number;
+ currency?: string;
+ fileExtensions?: string[];
+ fileExtensionsErrorMessage?: string;
}
export type ContentModelFieldValue =
| string
| number
| boolean
+ | string[]
| FieldSettings
| FieldSettingsOptions[];
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());
};
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",
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))
+ );
+};