Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/italia/publiccode-parser-go v1.2.4 h1:ASdOVjgCNtlRKW+/ZrPxmguUxjhKx74vv0TKwLH3U6M=
github.com/italia/publiccode-parser-go v1.2.4/go.mod h1:zYlDR8AbitTI9RzX3IRV73tqsmR0SOmhWCJDb3FpMT0=
github.com/italia/publiccode-parser-go/v3 v3.0.0/go.mod h1:MXFsgghRD+t6k+08WEeRLNrlTzvPo1AqIRL2tRB4tDE=
github.com/italia/publiccode-parser-go/v5 v5.1.1 h1:XVuWXNohNJfSvvXO3OjBoKJU09UELEtRXFpkSwOttvg=
github.com/italia/publiccode-parser-go/v5 v5.1.1/go.mod h1:xndoanQHcweEnJlubntvOHlT/cvde0eFDF59O5PwuCg=
github.com/italia/publiccode-parser-go/v5 v5.2.1 h1:9aDiCrh84nHAJzDRhf/Gx+exusfd4iQ0GCwtEwofeqo=
github.com/italia/publiccode-parser-go/v5 v5.2.1/go.mod h1:xndoanQHcweEnJlubntvOHlT/cvde0eFDF59O5PwuCg=
github.com/kyoh86/go-spdx v0.0.5-0.20220421143955-2f42f2d4c410/go.mod h1:0Ndah0G/f6NZOyvjm4hUmUGUjCKRzC1qirN4LKASBkM=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"prebuild": "npm run build:wasm && npm run build:licenses",
"build:providers-oembed": "tsx scripts/getProvidersOembed.ts src/generated/providers-oembed.json",
"build:licenses": "mkdir -p src/generated && tsx scripts/genLicenseList.ts src/generated/licenses.json",
"build:organisations": "tsx src/app/data/generateOrganisations.ts",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that there should be a note in the readme that this script should be run.

or run it as a post install script

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script is documented in the following README.

I added an additional post install script.

"build:wasm": "cp \"$(go env GOROOT)/misc/wasm/wasm_exec.js\" public && GOOS=js GOARCH=wasm go build -o public/main.wasm src/wasm/main.go",
"serve": "rm -rf dist; npm run build && http-server dist",
"test": "jest --passWithNoTests",
"gdeploy": "gh-pages -u 'Deploy Bot <no-reply@puzzle.ch>' -d dist",
"deploy": "gh-pages -u 'Deploy Bot <no-reply@teamdigitale.governo.it>' -d dist",
"release": "release-it",
"postinstall": "npm run build:organisations",
"_postinstall": "patch-package"
},
"keywords": [
Expand Down
40 changes: 37 additions & 3 deletions src/app/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import useFormPersist from "react-hook-form-persist";
import { useTranslation } from "react-i18next";
import { RequiredDeep } from "type-fest";
import licenses from "../../generated/licenses.json";
import { allLangs, displayName } from "../../i18n";
import organisationData from "../data/organisations.json";
import { allLangs, displayName, getLocalizedText } from "../../i18n";
import categories from "../contents/categories";
import { DEFAULT_COUNTRY_SECTIONS } from "../contents/constants";
import * as countrySection from "../contents/countrySpecificSection";
Expand Down Expand Up @@ -130,6 +131,7 @@ const resolver: Resolver<PublicCode | PublicCodeWithDeprecatedFields> = async (
const defaultValues = {
publiccodeYmlVersion: LATEST_VERSION,
legal: {},
organisation: {},
localisation: { availableLanguages: [] },
maintenance: { contacts: undefined, contractors: undefined },
platforms: [],
Expand All @@ -149,7 +151,7 @@ const isNotTheSameVersion = (version1: string, version2: string) => {

export default function Editor() {
//#region UI
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const { countrySections } = useCountryStore();
const { resetWarnings, setWarnings } = useWarningStore();
const {
Expand All @@ -164,6 +166,14 @@ export default function Editor() {
const { languages, setLanguages, resetLanguages } = useLanguagesStore();
const { setCountrySections } = useCountryStore();

const organisations = organisationData.flatMap(data =>
data.organisations.map(organisation => ({
text: getLocalizedText(organisation.name, i18n.language),
value: organisation.id,
group: getLocalizedText(data.name, i18n.language) + " (" + getLocalizedText(data.abbreviation, i18n.language) + ")",
}))
);

const getNestedValue = (
obj: PublicCodeWithDeprecatedFields,
path: string
Expand Down Expand Up @@ -249,14 +259,31 @@ export default function Editor() {
[setValue]
);

const updateOrganisation = useCallback(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the usage of useCallback necessary here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

I did not move the function inside useEffect to keep the code consistent with resetMaintenance.

(value: Partial<PublicCode>) => {
const uri = value.organisation?.uri;

if (uri) {
const organisation = organisations.find(o => o.value === uri);
setValue("organisation.name", organisation?.text);
} else {
setValue("organisation", undefined)
}
},
[organisations, setValue]
)

useEffect(() => {
const subscription = watch((value, { name }) => {
if (name === "maintenance.type") {
resetMaintenance(value as PublicCode);
}
if (name === "organisation.uri") {
updateOrganisation(value as PublicCode)
}
});
return () => subscription.unsubscribe();
}, [watch, resetMaintenance]);
}, [watch, resetMaintenance, updateOrganisation]);
//#endregion

//#region form action handlers
Expand Down Expand Up @@ -534,6 +561,13 @@ export default function Editor() {
<span>
<EditorInput<"landingURL"> fieldName="landingURL" />
</span>
<div className="mt-5">
<EditorSelect<"organisation.uri">
fieldName="organisation.uri"
data={organisations}
filter="contains"
/>
</div>
<span>
<EditorInput<"isBasedOn"> fieldName="isBasedOn" />
</span>
Expand Down
14 changes: 11 additions & 3 deletions src/app/components/EditorSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import PublicCode from "../contents/publiccode";
type Props<T> = {
fieldName: T;
required?: boolean;
data: Array<{ value: string; text: string }>;
filter?: Filter<{ value: string; text: string }>;
data: Array<{ value: string; text: string; group?: string }>;
filter?: Filter<{ value: string; text: string; group?: string }>;
};

export default function EditorSelect<
Expand Down Expand Up @@ -48,10 +48,18 @@ export default function EditorSelect<
onChange(value)
}}
value={value}
data={[...(!required ? [{ text: "(unset)", value: "" }] : []), ...data]}
data={[...(!required ? [{text: "", value: ""}] : []), ...data]}
dataKey="value"
textField="text"
renderListItem={(item) => {
if (item.value === "") {
return <span>(unset)</span>;
} else {
return <span>{item.text}</span>;
}
}}
filter={filter}
groupBy={"group"}
/>
<small className="form-text">{description}</small>
{errorMessage && (
Expand Down
11 changes: 11 additions & 0 deletions src/app/contents/fields/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ const fields = (): Array<Field> => {
section: 1,
widget: "url",
},
{
type: "array",
title: "uri",
section: 0,
items: {
type: "string",
enum: [],
},
widget: "combobox",
group: "organization",
},
{
title: "localisedName",
type: "string",
Expand Down
9 changes: 8 additions & 1 deletion src/app/contents/publiccode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import maintenanceTypes from "./maintenanceTypes";
import scopes from "./scopes";
import softwareTypes from "./softwareTypes";

export const LATEST_VERSION = "0.4.0"
export const LATEST_VERSION = "0.5.0"

// https://yml.publiccode.tools/schema.core.html
export default interface PublicCode {
Expand All @@ -13,6 +13,7 @@ export default interface PublicCode {
applicationSuite?: string;
url: string;
landingURL?: string;
organisation?: Organisation;
isBasedOn?: string;
softwareVersion?: string;
releaseDate?: string; // “YYYY-MM-DD”
Expand Down Expand Up @@ -72,6 +73,11 @@ interface Legal {
authorsFile?: string;
}

interface Organisation {
uri: string;
name?: string;
}

interface Maintenance {
type: (typeof maintenanceTypes)[number];
contractors?: Array<Contractor>;
Expand Down Expand Up @@ -186,6 +192,7 @@ export const publicCodeDummyObjectFactory = () => ({
applicationSuite: '',
url: '',
landingURL: '',
organisation: { uri: '' },
isBasedOn: '',
softwareVersion: '',
releaseDate: '',
Expand Down
21 changes: 21 additions & 0 deletions src/app/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Data

## Organisations

The data in the file `organisations.json` originates from the following sources:

- The seven departements and the Federal Chancellery (`departements.json`)
```
https://ld.admin.ch/sparql/#query=PREFIX%20schema%3A%20%3Chttp%3A%2F%2Fschema.org%2F%3E%0APREFIX%20rdf%3A%20%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E%0APREFIX%20rdfs%3A%20%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0A%0ASELECT%20DISTINCT%20%3Fdepartment%20%3FnameDepDe%20%3FnameDepFr%20%3FnameDepIt%20%3FnameDepEn%20%3FaltNameDepDe%20%3FaltNameDepFr%20%3FaltNameDepIt%20%3FaltNameDepEn%20%3Foffice%20%3FnameDe%20%3FnameFr%20%3FnameIt%20%3FnameEn%20WHERE%20%7B%0A%20%20%0A%20%20%3Fdepartment%20schema%3AinDefinedTermSet%20%3Chttps%3A%2F%2Fld.admin.ch%2Fdimension%2Fdepartment%3E.%0A%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDe.%20FILTER(lang(%3FnameDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameFr.%20FILTER(lang(%3FnameFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameIt.%20FILTER(lang(%3FnameIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameEn.%20FILTER(lang(%3FnameEn)%20%3D%20%22en%22)%20%7D%0A%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDe.%20FILTER(lang(%3FaltNameDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameFr.%20FILTER(lang(%3FaltNameFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameIt.%20FILTER(lang(%3FaltNameIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameEn.%20FILTER(lang(%3FaltNameEn)%20%3D%20%22en%22)%20%7D%0A%20%20%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepDe.%20FILTER(lang(%3FnameDepDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepFr.%20FILTER(lang(%3FnameDepFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepIt.%20FILTER(lang(%3FnameDepIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepEn.%20FILTER(lang(%3FnameDepEn)%20%3D%20%22en%22)%20%7D%0A%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepDe.%20FILTER(lang(%3FaltNameDepDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepFr.%20FILTER(lang(%3FaltNameDepFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepIt.%20FILTER(lang(%3FaltNameDepIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepEn.%20FILTER(lang(%3FaltNameDepEn)%20%3D%20%22en%22)%20%7D%0A%0A%7D%0A&endpoint=https%3A%2F%2Fld.admin.ch%2Fquery&requestMethod=POST&tabTitle=Query&headers=%7B%7D&contentTypeConstruct=text%2Fturtle&contentTypeSelect=application%2Fsparql-results%2Bjson&outputFormat=table&outputSettings=%7B%22isEllipsed%22%3Atrue%2C%22compact%22%3Afalse%7D
```

- All offices of the seven departments (`offices.json`)
```
https://ld.admin.ch/sparql/#query=PREFIX%20schema%3A%20%3Chttp%3A%2F%2Fschema.org%2F%3E%0APREFIX%20rdf%3A%20%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E%0APREFIX%20rdfs%3A%20%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0A%0ASELECT%20DISTINCT%20%3Fdepartment%20%3FnameDepDe%20%3FnameDepFr%20%3FnameDepIt%20%3FnameDepEn%20%3FaltNameDepDe%20%3FaltNameDepFr%20%3FaltNameDepIt%20%3FaltNameDepEn%20%3Foffice%20%3FnameDe%20%3FnameFr%20%3FnameIt%20%3FnameEn%20WHERE%20%7B%0A%20%0A%20%20%3Foffice%20schema%3AinDefinedTermSet%20%3Chttps%3A%2F%2Fld.admin.ch%2Foffice%3E.%0A%20%20%3Foffice%20schema%3AparentOrganization%20%3Fdepartment.%0A%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3Aname%20%3FnameDe.%20FILTER(lang(%3FnameDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3Aname%20%3FnameFr.%20FILTER(lang(%3FnameFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3Aname%20%3FnameIt.%20FILTER(lang(%3FnameIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3Aname%20%3FnameEn.%20FILTER(lang(%3FnameEn)%20%3D%20%22en%22)%20%7D%0A%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3AalternateName%20%3FaltNameDe.%20FILTER(lang(%3FaltNameDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3AalternateName%20%3FaltNameFr.%20FILTER(lang(%3FaltNameFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3AalternateName%20%3FaltNameIt.%20FILTER(lang(%3FaltNameIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Foffice%20schema%3AalternateName%20%3FaltNameEn.%20FILTER(lang(%3FaltNameEn)%20%3D%20%22en%22)%20%7D%0A%20%20%0A%20%20%3Fdepartment%20schema%3AinDefinedTermSet%20%3Chttps%3A%2F%2Fld.admin.ch%2Fdepartment%3E.%0A%20%20%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepDe.%20FILTER(lang(%3FnameDepDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepFr.%20FILTER(lang(%3FnameDepFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepIt.%20FILTER(lang(%3FnameDepIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3Aname%20%3FnameDepEn.%20FILTER(lang(%3FnameDepEn)%20%3D%20%22en%22)%20%7D%0A%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepDe.%20FILTER(lang(%3FaltNameDepDe)%20%3D%20%22de%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepFr.%20FILTER(lang(%3FaltNameDepFr)%20%3D%20%22fr%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepIt.%20FILTER(lang(%3FaltNameDepIt)%20%3D%20%22it%22)%20%7D%0A%20%20OPTIONAL%20%7B%20%3Fdepartment%20schema%3AalternateName%20%3FaltNameDepEn.%20FILTER(lang(%3FaltNameDepEn)%20%3D%20%22en%22)%20%7D%0A%0A%7D%0A&endpoint=https%3A%2F%2Fld.admin.ch%2Fquery&requestMethod=POST&tabTitle=Query&headers=%7B%7D&contentTypeConstruct=text%2Fturtle&contentTypeSelect=application%2Fsparql-results%2Bjson&outputFormat=table&outputSettings=%7B%22pageSize%22%3A-1%7D
```

It can be (re-)generated by running:

```bash
npx run build:organisations
```
Loading
Loading