Skip to content

Commit 4796597

Browse files
authored
feat: PublicCode v0.5.0 Support (#541)
1 parent 3f6c813 commit 4796597

File tree

18 files changed

+425
-66
lines changed

18 files changed

+425
-66
lines changed

go.work.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
1111
github.com/italia/publiccode-parser-go v1.2.4 h1:ASdOVjgCNtlRKW+/ZrPxmguUxjhKx74vv0TKwLH3U6M=
1212
github.com/italia/publiccode-parser-go v1.2.4/go.mod h1:zYlDR8AbitTI9RzX3IRV73tqsmR0SOmhWCJDb3FpMT0=
1313
github.com/italia/publiccode-parser-go/v3 v3.0.0/go.mod h1:MXFsgghRD+t6k+08WEeRLNrlTzvPo1AqIRL2tRB4tDE=
14+
github.com/italia/publiccode-parser-go/v5 v5.2.1 h1:9aDiCrh84nHAJzDRhf/Gx+exusfd4iQ0GCwtEwofeqo=
15+
github.com/italia/publiccode-parser-go/v5 v5.2.1/go.mod h1:xndoanQHcweEnJlubntvOHlT/cvde0eFDF59O5PwuCg=
1416
github.com/kyoh86/go-spdx v0.0.5-0.20220421143955-2f42f2d4c410/go.mod h1:0Ndah0G/f6NZOyvjm4hUmUGUjCKRzC1qirN4LKASBkM=
1517
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
1618
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=

src/app/components/Editor.tsx

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import EditorContractors from "./EditorContractors";
5151
import EditorDate from "./EditorDate";
5252
import EditorDescriptionInput from "./EditorDescriptionInput";
5353
import EditorFeatures from "./EditorFeatures";
54+
import EditorFundedBy from "./EditorFundedBy";
5455
import EditorInput from "./EditorInput";
5556
import EditorMultiselect from "./EditorMultiselect";
5657
import EditorRadio from "./EditorRadio";
@@ -95,7 +96,7 @@ const checkWarnings = async (values: PublicCode) => {
9596
};
9697

9798
const resolver: Resolver<PublicCode | PublicCodeWithDeprecatedFields> = async (
98-
values,
99+
values
99100
) => {
100101
console.log(values);
101102

@@ -130,7 +131,7 @@ const defaultValues = {
130131
localisation: { availableLanguages: [] },
131132
maintenance: { contacts: undefined, contractors: undefined },
132133
platforms: [],
133-
categories: [],
134+
categories: undefined,
134135
description: {},
135136
it: defaultItaly,
136137
};
@@ -164,7 +165,7 @@ export default function Editor() {
164165
useITCountrySpecific();
165166
const getNestedValue = (
166167
obj: PublicCodeWithDeprecatedFields,
167-
path: string,
168+
path: string
168169
) => {
169170
return path.split(".").reduce((acc, key) => (acc as never)?.[key], obj);
170171
};
@@ -197,6 +198,19 @@ export default function Editor() {
197198
} = getValues() as PublicCode;
198199
return type === "internal" || type === "community";
199200
};
201+
const isConformeVisible = () => {
202+
const values = getValues() as PublicCode;
203+
if (!values?.it?.conforme) {
204+
return false;
205+
}
206+
const conforme = values.it.conforme;
207+
return (
208+
conforme.lineeGuidaDesign !== undefined ||
209+
conforme.modelloInteroperabilita !== undefined ||
210+
conforme.misureMinimeSicurezza !== undefined ||
211+
conforme.gdpr !== undefined
212+
);
213+
};
200214
//#endregion
201215

202216
//#region form definition
@@ -223,15 +237,15 @@ export default function Editor() {
223237
const { countryExtensionVersion } = it;
224238
const isCountryExtensionVersionDefined = Boolean(countryExtensionVersion);
225239
const isDifferentFromSpecificDefinedValue = Boolean(
226-
IT_COUNTRY_EXTENSION_VERSION !== countryExtensionVersion,
240+
IT_COUNTRY_EXTENSION_VERSION !== countryExtensionVersion
227241
);
228242

229243
const countryExtensionVersionVisible =
230244
isCountryExtensionVersionDefined && isDifferentFromSpecificDefinedValue;
231245

232246
setShowCountryExtensionVersion(countryExtensionVersionVisible);
233247
},
234-
[],
248+
[]
235249
);
236250

237251
useFormPersist("form-values", {
@@ -243,7 +257,7 @@ export default function Editor() {
243257
checkPubliccodeYmlVersion(pc);
244258
checkItCountryExtensionVersion(pc);
245259
},
246-
[setLanguages],
260+
[setLanguages]
247261
),
248262
storage: window?.localStorage, // default window.sessionStorage
249263
exclude: [],
@@ -266,7 +280,7 @@ export default function Editor() {
266280
setValue("maintenance.contacts", undefined);
267281
}
268282
},
269-
[setValue],
283+
[setValue]
270284
);
271285

272286
useEffect(() => {
@@ -290,13 +304,13 @@ export default function Editor() {
290304
{
291305
dismissable: true,
292306
state: "success",
293-
},
307+
}
294308
);
295309
}
296310
},
297311
(e: FieldErrors<PublicCode>) => {
298312
const genericErrors = Object.entries(e).filter(([key]) =>
299-
key.startsWith("GenericError"),
313+
key.startsWith("GenericError")
300314
);
301315

302316
const body = genericErrors.length ? (
@@ -318,7 +332,7 @@ export default function Editor() {
318332
state: "error",
319333
});
320334
console.error("Errors:", e);
321-
},
335+
}
322336
);
323337

324338
const resetFormHandler = () => {
@@ -332,7 +346,7 @@ export default function Editor() {
332346
};
333347

334348
const setFormDataAfterImport = async (
335-
fetchData: () => Promise<PublicCode | null>,
349+
fetchData: () => Promise<PublicCode | null>
336350
) => {
337351
try {
338352
const publicCode = await fetchData().then((publicCode) => {
@@ -364,7 +378,7 @@ export default function Editor() {
364378
Array.from(res.warnings).map(([key, { message }]) => ({
365379
key,
366380
message,
367-
})),
381+
}))
368382
);
369383
} catch (error: unknown) {
370384
notify("Import error", (error as Error).message, {
@@ -487,7 +501,7 @@ export default function Editor() {
487501
</div>
488502
<div>
489503
{isDeprecatedFieldVisible(
490-
`description.${lang}.genericName` as never,
504+
`description.${lang}.genericName` as never
491505
) && (
492506
<span>
493507
<EditorDescriptionInput<"genericName">
@@ -565,6 +579,12 @@ export default function Editor() {
565579
<span>
566580
<EditorInput<"isBasedOn"> fieldName="isBasedOn" />
567581
</span>
582+
<span>
583+
<EditorInput<"organisation.uri"> fieldName="organisation.uri" />
584+
</span>
585+
<div className="mt-4 mb-4">
586+
<EditorFundedBy />
587+
</div>
568588
<span>
569589
<EditorInput<"softwareVersion"> fieldName="softwareVersion" />
570590
</span>
@@ -631,7 +651,6 @@ export default function Editor() {
631651
<EditorMultiselect<"categories">
632652
fieldName="categories"
633653
data={categories.map((e) => ({ text: e, value: e }))}
634-
required
635654
filter="contains"
636655
/>
637656
</span>
@@ -715,25 +734,39 @@ export default function Editor() {
715734
</div>
716735
</div>
717736
)}
718-
<div className="mt-4">
719-
<h5>{t("publiccodeyml.it.conforme.label")}</h5>
720-
<div className="row">
721-
<div className="col-md-6">
722-
<EditorBoolean<"it.conforme.lineeGuidaDesign"> fieldName="it.conforme.lineeGuidaDesign" />
737+
{isConformeVisible() && (
738+
<div className="mt-4">
739+
<h5>{t("publiccodeyml.it.conforme.label")}</h5>
740+
<div className="row">
741+
<div className="col-md-6">
742+
<EditorBoolean<"it.conforme.lineeGuidaDesign">
743+
fieldName="it.conforme.lineeGuidaDesign"
744+
deprecated
745+
/>
746+
</div>
747+
<div className="col-md-6">
748+
<EditorBoolean<"it.conforme.modelloInteroperabilita">
749+
fieldName="it.conforme.modelloInteroperabilita"
750+
deprecated
751+
/>
752+
</div>
723753
</div>
724-
<div className="col-md-6">
725-
<EditorBoolean<"it.conforme.modelloInteroperabilita"> fieldName="it.conforme.modelloInteroperabilita" />
754+
<div className="row">
755+
<div className="col-md-6">
756+
<EditorBoolean<"it.conforme.misureMinimeSicurezza">
757+
fieldName="it.conforme.misureMinimeSicurezza"
758+
deprecated
759+
/>
760+
</div>
761+
<div className="col-md-6">
762+
<EditorBoolean<"it.conforme.gdpr">
763+
fieldName="it.conforme.gdpr"
764+
deprecated
765+
/>
766+
</div>
726767
</div>
727768
</div>
728-
<div className="row">
729-
<div className="col-md-6">
730-
<EditorBoolean<"it.conforme.misureMinimeSicurezza"> fieldName="it.conforme.misureMinimeSicurezza" />
731-
</div>
732-
<div className="col-md-6">
733-
<EditorBoolean<"it.conforme.gdpr"> fieldName="it.conforme.gdpr" />
734-
</div>
735-
</div>
736-
</div>
769+
)}
737770
<div className="mt-4">
738771
<h5>{t("publiccodeyml.it.piattaforme.label")}</h5>
739772
<div className="row">
@@ -758,10 +791,13 @@ export default function Editor() {
758791
</div>
759792
</div>
760793
</div>
761-
<div className="mt-4">
794+
<div className="mt-4 mb-4">
762795
<h5>{t("publiccodeyml.it.riuso.label")}</h5>
763796
<div>
764-
<EditorInput<"it.riuso.codiceIPA"> fieldName="it.riuso.codiceIPA" />
797+
<EditorInput<"it.riuso.codiceIPA">
798+
fieldName="it.riuso.codiceIPA"
799+
deprecated
800+
/>
765801
</div>
766802
</div>
767803
</div>

src/app/components/EditorBoolean.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1+
import { FormGroup, Label } from "design-react-kit";
2+
import { get } from "lodash";
13
import {
24
FieldPathByValue,
35
useController,
46
useFormContext,
57
} from "react-hook-form";
6-
import Input from "./Input";
78
import { useTranslation } from "react-i18next";
8-
import PublicCode from "../contents/publiccode";
9-
import { FormGroup, Label } from "design-react-kit";
109
import { RequiredDeep } from "type-fest";
11-
import { get } from "lodash";
10+
import PublicCode from "../contents/publiccode";
11+
import Input from "./Input";
1212

1313
type Props<T> = {
1414
fieldName: T;
1515
required?: boolean;
16+
deprecated?: boolean;
1617
};
1718

1819
export default function EditorBoolean<
1920
T extends FieldPathByValue<RequiredDeep<PublicCode>, boolean>,
20-
>({ fieldName, required }: Props<T>): JSX.Element {
21+
>({ fieldName, required, deprecated }: Props<T>): JSX.Element {
2122
const { control } = useFormContext<PublicCode>();
2223
const {
2324
field,
@@ -33,7 +34,9 @@ export default function EditorBoolean<
3334

3435
return (
3536
<fieldset className="editor-boolean">
36-
<legend>{`${label}${required ? " *" : ""}`}</legend>
37+
<legend>{`${label}${required ? " *" : ""}${
38+
deprecated ? ` - ${t(`editor.form.deprecatedField`)}` : ""
39+
}`}</legend>
3740
<FormGroup check inline>
3841
<Input
3942
onBlur={field.onBlur}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { Button, Icon, Input, Table } from "design-react-kit";
2+
import { get } from "lodash";
3+
import { useController, useFieldArray, useFormContext } from "react-hook-form";
4+
import { useTranslation } from "react-i18next";
5+
6+
import PublicCode, {
7+
FundingOrganisation,
8+
defaultFundingOrganisation,
9+
} from "../contents/publiccode";
10+
11+
const fieldName = "fundedBy" as const;
12+
const subfields = ["name", "uri"] as const satisfies Readonly<
13+
Array<keyof FundingOrganisation>
14+
>;
15+
16+
export default function EditorFundedBy(): JSX.Element {
17+
const { control, register } = useFormContext<PublicCode, typeof fieldName>();
18+
const { append, fields, remove } = useFieldArray<
19+
PublicCode,
20+
typeof fieldName
21+
>({
22+
control,
23+
name: fieldName,
24+
});
25+
26+
const {
27+
field,
28+
formState: { errors },
29+
} = useController<PublicCode, typeof fieldName>({
30+
control,
31+
name: fieldName,
32+
});
33+
34+
const { t } = useTranslation();
35+
36+
return (
37+
<div className="mb-0">
38+
<div className="position-relative">
39+
<label className="description-label active">
40+
{t(`publiccodeyml.${fieldName}.label`)}
41+
</label>
42+
</div>
43+
<div className="ms-2">
44+
{field.value?.length === 0 ? (
45+
<p>
46+
<small>{t("editor.form.noFunders")}</small>
47+
</p>
48+
) : (
49+
<Table responsive>
50+
<thead>
51+
<tr>
52+
<th className="align-top">#</th>
53+
<th className="align-top">
54+
{t(`publiccodeyml.${fieldName}.name.label`)} *
55+
</th>
56+
<th className="align-top">
57+
{t(`publiccodeyml.${fieldName}.uri.label`)}
58+
</th>
59+
<th></th>
60+
</tr>
61+
</thead>
62+
<tbody>
63+
{fields.map(({ id }, index) => (
64+
<tr key={id}>
65+
<th scope="row">{index + 1}</th>
66+
{subfields.map((subfield) => {
67+
const { ref, ...reg } = register(
68+
`${fieldName}.${index}.${subfield}`
69+
);
70+
71+
return (
72+
<td key={subfield}>
73+
<Input
74+
{...reg}
75+
innerRef={ref}
76+
type={subfield === "uri" ? "url" : "text"}
77+
valid={
78+
get(errors, `${fieldName}.${index}.${subfield}`) &&
79+
false
80+
}
81+
validationText={get(
82+
errors,
83+
`${fieldName}.${index}.${subfield}.message`
84+
)}
85+
/>
86+
</td>
87+
);
88+
})}
89+
<td>
90+
<Button
91+
color="link"
92+
icon
93+
onClick={() => remove(index)}
94+
size="xs"
95+
>
96+
<Icon icon="it-delete" size="sm" title="Remove funder" />
97+
</Button>
98+
</td>
99+
</tr>
100+
))}
101+
</tbody>
102+
</Table>
103+
)}
104+
<Button
105+
color="primary"
106+
onClick={() => append({ ...defaultFundingOrganisation })}
107+
>
108+
{t("editor.form.addnew")}
109+
</Button>
110+
</div>
111+
</div>
112+
);
113+
}

0 commit comments

Comments
 (0)