chore: update biome schema and dependencies, refactor key usage in components
- Updated biome schema version from 2.2.2 to 2.3.1 in biome.json. - Removed experimentalScannerIgnores from files section and replaced with ignore patterns. - Added CSS parser configuration for Tailwind directives. - Updated linter rules for better code quality. - Refactored key usage in various components to use unique identifiers instead of array indices. - Updated package-lock.json and package.json to reflect new biome version. - General code cleanup and improvements across multiple components for better maintainability.
This commit is contained in:
parent
b83527eb97
commit
f85ea22215
29 changed files with 143 additions and 122 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/2.2.2/schema.json",
|
"$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
|
||||||
"assist": {
|
"assist": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"source": {
|
"source": {
|
||||||
|
|
@ -11,19 +11,6 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"files": {
|
"files": {
|
||||||
"experimentalScannerIgnores": [
|
|
||||||
"node_modules",
|
|
||||||
"dist",
|
|
||||||
"build",
|
|
||||||
".next",
|
|
||||||
".git",
|
|
||||||
"public",
|
|
||||||
".github",
|
|
||||||
".vscode",
|
|
||||||
"certificates",
|
|
||||||
"headers",
|
|
||||||
"schemas"
|
|
||||||
],
|
|
||||||
"ignoreUnknown": true,
|
"ignoreUnknown": true,
|
||||||
"includes": [
|
"includes": [
|
||||||
"**",
|
"**",
|
||||||
|
|
@ -52,7 +39,18 @@
|
||||||
"!src/i18n/comuni.ts",
|
"!src/i18n/comuni.ts",
|
||||||
"!src/i18n/nazioni.ts",
|
"!src/i18n/nazioni.ts",
|
||||||
"!src/i18n/provincie.ts",
|
"!src/i18n/provincie.ts",
|
||||||
"!src/styles/globals.css"
|
"!src/styles/globals.css",
|
||||||
|
"!!**/node_modules",
|
||||||
|
"!!**/dist",
|
||||||
|
"!!**/build",
|
||||||
|
"!!**/.next",
|
||||||
|
"!!**/.git",
|
||||||
|
"!!**/public",
|
||||||
|
"!!**/.github",
|
||||||
|
"!!**/.vscode",
|
||||||
|
"!!**/certificates",
|
||||||
|
"!!**/headers",
|
||||||
|
"!!**/schemas"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
|
@ -64,6 +62,11 @@
|
||||||
"quoteStyle": "double"
|
"quoteStyle": "double"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css": {
|
||||||
|
"parser": {
|
||||||
|
"tailwindDirectives": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"domains": {
|
"domains": {
|
||||||
"next": "recommended",
|
"next": "recommended",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"noUndeclaredVariables": "on",
|
"noUndeclaredVariables": "on",
|
||||||
"useExhaustiveDependencies": "off",
|
"useExhaustiveDependencies": "off",
|
||||||
"useHookAtTopLevel": "off",
|
"useHookAtTopLevel": "off",
|
||||||
"useParseIntRadix": "off"
|
"useParseIntRadix": "off",
|
||||||
|
"noChildrenProp": "error",
|
||||||
|
"noReactPropAssignments": "error"
|
||||||
},
|
},
|
||||||
"nursery": {
|
"nursery": {
|
||||||
"useExhaustiveSwitchCases": "on",
|
"useExhaustiveSwitchCases": "on",
|
||||||
|
|
@ -111,7 +116,7 @@
|
||||||
"useThrowOnlyError": "on"
|
"useThrowOnlyError": "on"
|
||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noArrayIndexKey": "off",
|
"noArrayIndexKey": "info",
|
||||||
"noDocumentImportInPage": "on",
|
"noDocumentImportInPage": "on",
|
||||||
"noHeadImportInDocument": "on",
|
"noHeadImportInDocument": "on",
|
||||||
"noUnknownAtRules": "off",
|
"noUnknownAtRules": "off",
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,12 @@ const OnboardingServizio = ({
|
||||||
</Button>
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
<Section>
|
<Section>
|
||||||
{annunci?.map((annuncio, index) => (
|
{annunci?.map((annuncio) => (
|
||||||
<>
|
<>
|
||||||
<Text className="font-semibold">
|
<Text className="font-semibold">
|
||||||
Annunci selezionati per te:
|
Annunci selezionati per te:
|
||||||
</Text>
|
</Text>
|
||||||
<Section className="mb-[30px]" key={index}>
|
<Section className="mb-[30px]" key={annuncio.codice}>
|
||||||
<Row className="mb-[12px]">
|
<Row className="mb-[12px]">
|
||||||
<Img
|
<Img
|
||||||
alt={`Annuncio image - ${annuncio.codice}`}
|
alt={`Annuncio image - ${annuncio.codice}`}
|
||||||
|
|
|
||||||
72
apps/infoalloggi/package-lock.json
generated
72
apps/infoalloggi/package-lock.json
generated
|
|
@ -91,7 +91,7 @@
|
||||||
"zod": "^4.1.12"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.2",
|
"@biomejs/biome": "^2.3.1",
|
||||||
"@hookform/devtools": "^4.4.0",
|
"@hookform/devtools": "^4.4.0",
|
||||||
"@react-email/preview-server": "^4.2.8",
|
"@react-email/preview-server": "^4.2.8",
|
||||||
"@total-typescript/ts-reset": "^0.6.1",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
|
|
@ -1044,9 +1044,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
"node_modules/@biomejs/biome": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.1.tgz",
|
||||||
"integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==",
|
"integrity": "sha512-A29evf1R72V5bo4o2EPxYMm5mtyGvzp2g+biZvRFx29nWebGyyeOSsDWGx3tuNNMFRepGwxmA9ZQ15mzfabK2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -1060,20 +1060,20 @@
|
||||||
"url": "https://opencollective.com/biome"
|
"url": "https://opencollective.com/biome"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@biomejs/cli-darwin-arm64": "2.2.2",
|
"@biomejs/cli-darwin-arm64": "2.3.1",
|
||||||
"@biomejs/cli-darwin-x64": "2.2.2",
|
"@biomejs/cli-darwin-x64": "2.3.1",
|
||||||
"@biomejs/cli-linux-arm64": "2.2.2",
|
"@biomejs/cli-linux-arm64": "2.3.1",
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.2.2",
|
"@biomejs/cli-linux-arm64-musl": "2.3.1",
|
||||||
"@biomejs/cli-linux-x64": "2.2.2",
|
"@biomejs/cli-linux-x64": "2.3.1",
|
||||||
"@biomejs/cli-linux-x64-musl": "2.2.2",
|
"@biomejs/cli-linux-x64-musl": "2.3.1",
|
||||||
"@biomejs/cli-win32-arm64": "2.2.2",
|
"@biomejs/cli-win32-arm64": "2.3.1",
|
||||||
"@biomejs/cli-win32-x64": "2.2.2"
|
"@biomejs/cli-win32-x64": "2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.1.tgz",
|
||||||
"integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==",
|
"integrity": "sha512-ombSf3MnTUueiYGN1SeI9tBCsDUhpWzOwS63Dove42osNh0PfE1cUtHFx6eZ1+MYCCLwXzlFlYFdrJ+U7h6LcA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1088,9 +1088,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
"node_modules/@biomejs/cli-darwin-x64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.1.tgz",
|
||||||
"integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==",
|
"integrity": "sha512-pcOfwyoQkrkbGvXxRvZNe5qgD797IowpJPovPX5biPk2FwMEV+INZqfCaz4G5bVq9hYnjwhRMamg11U4QsRXrQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1105,9 +1105,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
"node_modules/@biomejs/cli-linux-arm64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.1.tgz",
|
||||||
"integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==",
|
"integrity": "sha512-td5O8pFIgLs8H1sAZsD6v+5quODihyEw4nv2R8z7swUfIK1FKk+15e4eiYVLcAE4jUqngvh4j3JCNgg0Y4o4IQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1122,9 +1122,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.1.tgz",
|
||||||
"integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==",
|
"integrity": "sha512-+DZYv8l7FlUtTrWs1Tdt1KcNCAmRO87PyOnxKGunbWm5HKg1oZBSbIIPkjrCtDZaeqSG1DiGx7qF+CPsquQRcg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1139,9 +1139,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
"node_modules/@biomejs/cli-linux-x64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.1.tgz",
|
||||||
"integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==",
|
"integrity": "sha512-PYWgEO7up7XYwSAArOpzsVCiqxBCXy53gsReAb1kKYIyXaoAlhBaBMvxR/k2Rm9aTuZ662locXUmPk/Aj+Xu+Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1156,9 +1156,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.1.tgz",
|
||||||
"integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==",
|
"integrity": "sha512-Y3Ob4nqgv38Mh+6EGHltuN+Cq8aj/gyMTJYzkFZV2AEj+9XzoXB9VNljz9pjfFNHUxvLEV4b55VWyxozQTBaUQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1173,9 +1173,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
"node_modules/@biomejs/cli-win32-arm64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.1.tgz",
|
||||||
"integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==",
|
"integrity": "sha512-RHIG/zgo+69idUqVvV3n8+j58dKYABRpMyDmfWu2TITC+jwGPiEaT0Q3RKD+kQHiS80mpBrST0iUGeEXT0bU9A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1190,9 +1190,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
"node_modules/@biomejs/cli-win32-x64": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.1.tgz",
|
||||||
"integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==",
|
"integrity": "sha512-izl30JJ5Dp10mi90Eko47zhxE6pYyWPcnX1NQxKpL/yMhXxf95oLTzfpu4q+MDBh/gemNqyJEwjBpe0MT5iWPA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
"zod": "^4.1.12"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.2",
|
"@biomejs/biome": "^2.3.1",
|
||||||
"@hookform/devtools": "^4.4.0",
|
"@hookform/devtools": "^4.4.0",
|
||||||
"@react-email/preview-server": "^4.2.8",
|
"@react-email/preview-server": "^4.2.8",
|
||||||
"@total-typescript/ts-reset": "^0.6.1",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ export default function AnnunciList({ annunci }: AnnunciProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul>
|
<ul>
|
||||||
{allAnnunci.map((ann, i) => (
|
{allAnnunci.map((ann) => (
|
||||||
<li key={i}>{ann.codice}</li>
|
<li key={ann.codice}>{ann.codice}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ export const AccordionComp = forwardRef<HTMLDivElement, AccordionCompProps>(
|
||||||
>
|
>
|
||||||
<Accordion className="w-full" collapsible type="single">
|
<Accordion className="w-full" collapsible type="single">
|
||||||
{texts.map((text, index) => (
|
{texts.map((text, index) => (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is safe here>
|
||||||
<AccordionItem key={`ac-item-${index}`} value={`item-${index}`}>
|
<AccordionItem key={`ac-item-${index}`} value={`item-${index}`}>
|
||||||
<AccordionTrigger className="cursor-pointer gap-4 text-left text-lg">
|
<AccordionTrigger className="cursor-pointer gap-4 text-left text-lg">
|
||||||
{text.title}
|
{text.title}
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ function Receipt({ data }: ReceiptProps) {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{data.items.map((item, idx) => (
|
{data.items.map((item) => (
|
||||||
<tr className="border-b" key={idx}>
|
<tr className="border-b" key={item.description}>
|
||||||
<td className="py-2">{item.description}</td>
|
<td className="py-2">{item.description}</td>
|
||||||
<td className="py-2 text-right">
|
<td className="py-2 text-right">
|
||||||
{formatCurrency(item.price)}
|
{formatCurrency(item.price)}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ export const CardAnnuncio = ({
|
||||||
<Carousel opts={{ loop: true }}>
|
<Carousel opts={{ loop: true }}>
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
{images?.map((img, idx) => (
|
{images?.map((img, idx) => (
|
||||||
<CarouselItem className="" key={`${img}-${idx}`}>
|
<CarouselItem className="" key={`${img.img}key`}>
|
||||||
<ImageFlbk
|
<ImageFlbk
|
||||||
alt={t.card.alt_immagine}
|
alt={t.card.alt_immagine}
|
||||||
blurDataURL={`/storage-api/get/${img.thumb}?image=true`}
|
blurDataURL={`/storage-api/get/${img.thumb}?image=true`}
|
||||||
|
|
@ -101,7 +101,7 @@ export const CardAnnuncio = ({
|
||||||
/>
|
/>
|
||||||
</CarouselItem>
|
</CarouselItem>
|
||||||
))}
|
))}
|
||||||
{videos?.map((video, idx) => {
|
{videos?.map((video) => {
|
||||||
if (!video) return null;
|
if (!video) return null;
|
||||||
if (video.includes("youtu")) {
|
if (video.includes("youtu")) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -123,7 +123,7 @@ export const CardAnnuncio = ({
|
||||||
? `/storage-api/get/${images[0].img}?image=true`
|
? `/storage-api/get/${images[0].img}?image=true`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
key={`videoplayer-${idx}`}
|
key={`videoplayer-${video}`}
|
||||||
videoSrc={video}
|
videoSrc={video}
|
||||||
/>
|
/>
|
||||||
</CarouselItem>
|
</CarouselItem>
|
||||||
|
|
@ -268,7 +268,7 @@ export const CarouselAnnuncio = ({
|
||||||
? ""
|
? ""
|
||||||
: !single && "md:basis-1/2 lg:basis-1/3",
|
: !single && "md:basis-1/2 lg:basis-1/3",
|
||||||
)}
|
)}
|
||||||
key={`${img}-${idx}-cF`}
|
key={`${img}-cF`}
|
||||||
>
|
>
|
||||||
<ImageFlbk
|
<ImageFlbk
|
||||||
alt={img}
|
alt={img}
|
||||||
|
|
@ -348,7 +348,7 @@ export const CarouselAnnuncio = ({
|
||||||
<Carousel opts={{ loop: true, startIndex: idxModal }}>
|
<Carousel opts={{ loop: true, startIndex: idxModal }}>
|
||||||
<CarouselContent>
|
<CarouselContent>
|
||||||
{immagini?.map((img, idx) => (
|
{immagini?.map((img, idx) => (
|
||||||
<CarouselItem key={`${img}-${idx}-cD`}>
|
<CarouselItem key={`${img}-cD`}>
|
||||||
<Image
|
<Image
|
||||||
alt={`carousel-img-${idx}`}
|
alt={`carousel-img-${idx}`}
|
||||||
className="mx-auto h-[90vh] w-full rounded-md object-contain sm:w-[80vw]"
|
className="mx-auto h-[90vh] w-full rounded-md object-contain sm:w-[80vw]"
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const EmailAccordion = ({ userId }: { userId: UsersId }) => {
|
||||||
{data.map((email, index) => (
|
{data.map((email, index) => (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
className="space-y-2 border-none"
|
className="space-y-2 border-none"
|
||||||
key={index}
|
key={email.title}
|
||||||
value={String(index)}
|
value={String(index)}
|
||||||
>
|
>
|
||||||
<AccordionTrigger className="rounded-md bg-muted px-4 py-2">
|
<AccordionTrigger className="rounded-md bg-muted px-4 py-2">
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,7 @@ const Typers = ({
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{currentTypers.map((typer, index) => {
|
{currentTypers.map((typer) => {
|
||||||
if (typer.userId === userId) return null;
|
if (typer.userId === userId) return null;
|
||||||
const variant = "received";
|
const variant = "received";
|
||||||
|
|
||||||
|
|
@ -319,7 +319,7 @@ const Typers = ({
|
||||||
className="flex flex-col gap-2 py-2"
|
className="flex flex-col gap-2 py-2"
|
||||||
exit={{ opacity: 0, scale: 1, x: 0, y: 1 }}
|
exit={{ opacity: 0, scale: 1, x: 0, y: 1 }}
|
||||||
initial={{ opacity: 0, scale: 1, x: 0, y: 50 }}
|
initial={{ opacity: 0, scale: 1, x: 0, y: 50 }}
|
||||||
key={`typers${messageLenght}${index}`}
|
key={`typers${messageLenght}${typer.userId}`}
|
||||||
layout
|
layout
|
||||||
style={{ originX: 0.5, originY: 0.5 }}
|
style={{ originX: 0.5, originY: 0.5 }}
|
||||||
transition={{
|
transition={{
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ const UserStatus = ({
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
{onlineStatus.map((user, idx) =>
|
{onlineStatus.map((user, idx) =>
|
||||||
user.isAdmin ? (
|
user.isAdmin ? (
|
||||||
<TooltipProvider delayDuration={0} key={idx}>
|
<TooltipProvider delayDuration={0} key={user.userId}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<span
|
<span
|
||||||
|
|
@ -160,7 +160,7 @@ const UserStatus = ({
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
className={cn("flex flex-row font-bold text-green-500 text-xs")}
|
className={cn("flex flex-row font-bold text-green-500 text-xs")}
|
||||||
key={idx}
|
key={user.userId}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{user.nome}
|
{user.nome}
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,14 @@ export const ComeFunziona = () => {
|
||||||
{t.come_funziona.title}
|
{t.come_funziona.title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mx-auto mt-8 w-full space-y-12 md:mt-16">
|
<div className="mx-auto mt-8 w-full space-y-12 md:mt-16">
|
||||||
{features.map((feature, idx) => (
|
{features.map((feature) => (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center gap-x-8 gap-y-6 lg:flex-row lg:odd:flex-row-reverse"
|
className="flex flex-col items-center gap-x-8 gap-y-6 lg:flex-row lg:odd:flex-row-reverse"
|
||||||
key={idx}
|
key={feature.title}
|
||||||
>
|
>
|
||||||
<div className="flex shrink-0 basis-1/2 justify-center">
|
<div className="flex shrink-0 basis-1/2 justify-center">
|
||||||
<Image
|
<Image
|
||||||
alt={`img${idx}`}
|
alt={`img${feature.title}`}
|
||||||
className="aspect-[6/4] rounded-xl border border-border/50"
|
className="aspect-[6/4] rounded-xl border border-border/50"
|
||||||
height={200}
|
height={200}
|
||||||
src={feature.image}
|
src={feature.image}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ export const AnimatedButton = forwardRef<
|
||||||
`absolute top-1/2 left-1/2 opacity-0 sparkle-${index}`,
|
`absolute top-1/2 left-1/2 opacity-0 sparkle-${index}`,
|
||||||
sparklesClassName,
|
sparklesClassName,
|
||||||
)}
|
)}
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is safe here>
|
||||||
key={index}
|
key={index}
|
||||||
type={sparklesIcon}
|
type={sparklesIcon}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,14 @@ export const StaggeredFade: React.FC<TextStaggeredFadeProps> = ({
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
>
|
>
|
||||||
{letters.map((word, i) => (
|
{letters.map((word, i) => (
|
||||||
<motion.span custom={i} key={`${word}-${i}`} variants={variants}>
|
<motion.span
|
||||||
|
custom={i}
|
||||||
|
key={`${word}-${
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is safe here>
|
||||||
|
i
|
||||||
|
}`}
|
||||||
|
variants={variants}
|
||||||
|
>
|
||||||
{word}
|
{word}
|
||||||
</motion.span>
|
</motion.span>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,11 @@ export const EtichetteModal = ({
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{data.map((etichetta, idx) => (
|
{data.map((etichetta) => (
|
||||||
<div className="flex gap-2" key={idx}>
|
<div
|
||||||
|
className="flex gap-2"
|
||||||
|
key={`${etichetta.id_etichetta}${chatId}`}
|
||||||
|
>
|
||||||
<Etichetta
|
<Etichetta
|
||||||
color_hex={etichetta.color_hex}
|
color_hex={etichetta.color_hex}
|
||||||
title={etichetta.title}
|
title={etichetta.title}
|
||||||
|
|
@ -128,8 +131,11 @@ export const EtichetteModal = ({
|
||||||
<SelectValue placeholder="Seleziona" />
|
<SelectValue placeholder="Seleziona" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{etichette?.map((etichetta, idx) => (
|
{etichette?.map((etichetta) => (
|
||||||
<SelectItem key={idx} value={String(etichetta.id_etichetta)}>
|
<SelectItem
|
||||||
|
key={etichetta.id_etichetta}
|
||||||
|
value={String(etichetta.id_etichetta)}
|
||||||
|
>
|
||||||
{etichetta.title}
|
{etichetta.title}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ const MapComp = ({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isGroup ? (
|
{isGroup ? (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is acceptable here>
|
||||||
groupData.map((pos, i) => <MarkerComp key={i} pos={pos} />)
|
groupData.map((pos, i) => <MarkerComp key={i} pos={pos} />)
|
||||||
) : (
|
) : (
|
||||||
<MarkerComp pos={posix} />
|
<MarkerComp pos={posix} />
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,8 @@ const Items = ({
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
{items?.map((item, idx) => (
|
{items?.map((item) => (
|
||||||
<DropdownMenuItem className="p-0" key={idx}>
|
<DropdownMenuItem className="p-0" key={item.href}>
|
||||||
<Link
|
<Link
|
||||||
aria-label={`Link to ${item.title}`}
|
aria-label={`Link to ${item.title}`}
|
||||||
className="flex h-full w-full items-center gap-3 px-2 py-1.5"
|
className="flex h-full w-full items-center gap-3 px-2 py-1.5"
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ export const PricingComponent = ({
|
||||||
"h-9 xs:h-10 rounded-md border-none px-3 xs:px-4 xs:py-2",
|
"h-9 xs:h-10 rounded-md border-none px-3 xs:px-4 xs:py-2",
|
||||||
idx === Value ? "fade-in-0 animate-in" : "",
|
idx === Value ? "fade-in-0 animate-in" : "",
|
||||||
)}
|
)}
|
||||||
key={idx}
|
key={opzione}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
ClearCarousel();
|
ClearCarousel();
|
||||||
setValue(idx);
|
setValue(idx);
|
||||||
|
|
@ -294,7 +294,7 @@ export const PricingComponentConsulenza = ({
|
||||||
"h-9 xs:h-10 rounded-md border-none px-3 xs:px-4 xs:py-2",
|
"h-9 xs:h-10 rounded-md border-none px-3 xs:px-4 xs:py-2",
|
||||||
idx === Value ? "fade-in-0 animate-in" : "",
|
idx === Value ? "fade-in-0 animate-in" : "",
|
||||||
)}
|
)}
|
||||||
key={idx}
|
key={opzione}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
ClearCarousel();
|
ClearCarousel();
|
||||||
setValue(idx);
|
setValue(idx);
|
||||||
|
|
|
||||||
|
|
@ -209,13 +209,12 @@ function SchedaAnnuncio({ data }: { data: Annunci }) {
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-4 gap-2 text-sm">
|
<div className="grid grid-cols-4 gap-2 text-sm">
|
||||||
{data.caratteristiche &&
|
{data.caratteristiche &&
|
||||||
filteredCaratteristiche(data.caratteristiche).map(
|
filteredCaratteristiche(data.caratteristiche).map((item) => {
|
||||||
(item, index) => {
|
|
||||||
if (!item.value) return null;
|
if (!item.value) return null;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-start gap-2"
|
className="flex items-center justify-start gap-2"
|
||||||
key={index}
|
key={item.text}
|
||||||
>
|
>
|
||||||
<span className="">
|
<span className="">
|
||||||
<IconMatrix type={item.icon} />
|
<IconMatrix type={item.icon} />
|
||||||
|
|
@ -225,8 +224,7 @@ function SchedaAnnuncio({ data }: { data: Annunci }) {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
})}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export const TabRicerca = ({
|
||||||
<Row
|
<Row
|
||||||
defaultOpen={getDefaultOpen(idx)}
|
defaultOpen={getDefaultOpen(idx)}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
key={idx}
|
key={servizio.servizio_id}
|
||||||
servizio={servizio}
|
servizio={servizio}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,13 @@ export const TabServizio = ({
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{data.map((servizio, idx) => {
|
{data.map((servizio) => {
|
||||||
const onboardDone =
|
const onboardDone =
|
||||||
servizio.isInterrotto ||
|
servizio.isInterrotto ||
|
||||||
servizio.isOkAcconto ||
|
servizio.isOkAcconto ||
|
||||||
servizio.decorrenza !== null;
|
servizio.decorrenza !== null;
|
||||||
return (
|
return (
|
||||||
<TableRow key={idx}>
|
<TableRow key={servizio.servizio_id}>
|
||||||
<TableCell className="max-w-20 truncate font-medium">
|
<TableCell className="max-w-20 truncate font-medium">
|
||||||
{servizio.servizio_id}
|
{servizio.servizio_id}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
||||||
|
|
@ -501,6 +501,7 @@ function YearGrid({
|
||||||
"bg-accent font-medium text-accent-foreground",
|
"bg-accent font-medium text-accent-foreground",
|
||||||
)}
|
)}
|
||||||
disabled={navView === "years" ? isDisabled : undefined}
|
disabled={navView === "years" ? isDisabled : undefined}
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is acceptable here>
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setNavView("days");
|
setNavView("days");
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ function Slider({
|
||||||
thumbClassName,
|
thumbClassName,
|
||||||
)}
|
)}
|
||||||
data-slot="slider-thumb"
|
data-slot="slider-thumb"
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <index is safe here>
|
||||||
key={index}
|
key={index}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -248,8 +248,8 @@ export const UploadComponent = ({
|
||||||
</FileUploadTrigger>
|
</FileUploadTrigger>
|
||||||
</FileUploadDropzone>
|
</FileUploadDropzone>
|
||||||
<FileUploadList>
|
<FileUploadList>
|
||||||
{files.map((file, index) => (
|
{files.map((file) => (
|
||||||
<FileUploadItem key={index} value={file}>
|
<FileUploadItem key={file.name} value={file}>
|
||||||
<FileUploadItemPreview />
|
<FileUploadItemPreview />
|
||||||
<FileUploadItemMetadata />
|
<FileUploadItemMetadata />
|
||||||
<FileUploadItemDelete asChild>
|
<FileUploadItemDelete asChild>
|
||||||
|
|
|
||||||
|
|
@ -479,12 +479,12 @@ export const FormNewServizio = ({
|
||||||
</Label>
|
</Label>
|
||||||
<FormDescription>{t.preferenze.altrepref_desc}</FormDescription>
|
<FormDescription>{t.preferenze.altrepref_desc}</FormDescription>
|
||||||
<div className="mt-2 flex flex-wrap gap-2">
|
<div className="mt-2 flex flex-wrap gap-2">
|
||||||
{altrePrefMapping.map((pair, index) => {
|
{altrePrefMapping.map((pair) => {
|
||||||
if (pair) {
|
if (pair) {
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
key={index}
|
key={pair[0]}
|
||||||
name={pair[0]}
|
name={pair[0]}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
|
|
|
||||||
|
|
@ -776,12 +776,12 @@ export const FormNewServizioAcquisto = ({
|
||||||
</Label>
|
</Label>
|
||||||
<FormDescription>{t.preferenze.altrepref_desc}</FormDescription>
|
<FormDescription>{t.preferenze.altrepref_desc}</FormDescription>
|
||||||
<div className="mt-2 flex flex-wrap gap-2">
|
<div className="mt-2 flex flex-wrap gap-2">
|
||||||
{altrePrefMapping.map((pair, index) => {
|
{altrePrefMapping.map((pair) => {
|
||||||
if (pair) {
|
if (pair) {
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
key={index}
|
key={pair[0]}
|
||||||
name={pair[0]}
|
name={pair[0]}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
|
|
|
||||||
|
|
@ -267,8 +267,8 @@ const FatturazioneData = ({ cf }: { cf: string }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data.data && data.data.length > 0 ? (
|
{data.data && data.data.length > 0 ? (
|
||||||
data.data.map((client, idx) => (
|
data.data.map((client, i) => (
|
||||||
<div className="space-y-4" key={idx}>
|
<div className="space-y-4" key={client.id || i}>
|
||||||
<h3 className="font-semibold text-lg">Dati Fatturazione</h3>
|
<h3 className="font-semibold text-lg">Dati Fatturazione</h3>
|
||||||
<p>Nome: {client.name}</p>
|
<p>Nome: {client.name}</p>
|
||||||
<p>Codice Fiscale: {client.tax_code}</p>
|
<p>Codice Fiscale: {client.tax_code}</p>
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,10 @@ const FAQSection = () => {
|
||||||
|
|
||||||
<div className="flex w-full max-w-6xl justify-between gap-5">
|
<div className="flex w-full max-w-6xl justify-between gap-5">
|
||||||
<div className="hidden flex-col gap-5 md:flex">
|
<div className="hidden flex-col gap-5 md:flex">
|
||||||
{t.faq.domande.map((item, index) => (
|
{t.faq.domande.map((item) => (
|
||||||
<Button
|
<Button
|
||||||
className="justify-start font-semibold text-lg"
|
className="justify-start font-semibold text-lg"
|
||||||
key={index}
|
key={item.id}
|
||||||
onClick={() => handleScroll(`faq-${item.id}`)}
|
onClick={() => handleScroll(`faq-${item.id}`)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
|
|
@ -74,11 +74,11 @@ const FAQSection = () => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-5">
|
<div className="flex w-full flex-col gap-5">
|
||||||
{t.faq.domande.map((item, index) => (
|
{t.faq.domande.map((item) => (
|
||||||
<Accordion
|
<Accordion
|
||||||
className="relative w-full rounded-lg bg-neutral-50 p-4"
|
className="relative w-full rounded-lg bg-neutral-50 p-4"
|
||||||
collapsible
|
collapsible
|
||||||
key={index}
|
key={item.id}
|
||||||
//defaultValue="0"
|
//defaultValue="0"
|
||||||
type="single"
|
type="single"
|
||||||
>
|
>
|
||||||
|
|
@ -86,7 +86,7 @@ const FAQSection = () => {
|
||||||
<h3 className="font-semibold text-2xl">{item.label}</h3>
|
<h3 className="font-semibold text-2xl">{item.label}</h3>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{item.qas.map((f, i) => (
|
{item.qas.map((f, i) => (
|
||||||
<AccordionItem key={i} value={i.toString()}>
|
<AccordionItem key={item.id} value={i.toString()}>
|
||||||
<AccordionTrigger className="text-left">
|
<AccordionTrigger className="text-left">
|
||||||
{f.title}
|
{f.title}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ const Prezzi: NextPage = () => {
|
||||||
<p>{t.prezzi.desc_transitori}</p>
|
<p>{t.prezzi.desc_transitori}</p>
|
||||||
<div className="my-5">
|
<div className="my-5">
|
||||||
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
||||||
{t.prezzi.cta_transitori.map((item, idx) => (
|
{t.prezzi.cta_transitori.map((item) => (
|
||||||
<li className="flex gap-x-4" key={idx}>
|
<li className="flex gap-x-4" key={item.titolo}>
|
||||||
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-sky-50 text-sky-600">
|
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-sky-50 text-sky-600">
|
||||||
<IconMatrix type={item.icon} />
|
<IconMatrix type={item.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -119,8 +119,8 @@ const Prezzi: NextPage = () => {
|
||||||
<p>{t.prezzi.desc_stabile}</p>
|
<p>{t.prezzi.desc_stabile}</p>
|
||||||
<div className="my-5">
|
<div className="my-5">
|
||||||
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
||||||
{t.prezzi.cta_stabile.map((item, idx) => (
|
{t.prezzi.cta_stabile.map((item) => (
|
||||||
<li className="flex gap-x-4" key={idx}>
|
<li className="flex gap-x-4" key={item.titolo}>
|
||||||
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-orange-50 text-orange-600">
|
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-orange-50 text-orange-600">
|
||||||
<IconMatrix type={item.icon} />
|
<IconMatrix type={item.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -166,8 +166,8 @@ const Prezzi: NextPage = () => {
|
||||||
<p>{t.prezzi.desc_contratti}</p>
|
<p>{t.prezzi.desc_contratti}</p>
|
||||||
<div className="my-5">
|
<div className="my-5">
|
||||||
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
<ul className="grid gap-x-12 gap-y-8 sm:grid-cols-2 lg:grid-cols-2">
|
||||||
{t.prezzi.cta_contratti.map((item, idx) => (
|
{t.prezzi.cta_contratti.map((item) => (
|
||||||
<li className="flex gap-x-4" key={idx}>
|
<li className="flex gap-x-4" key={item.titolo}>
|
||||||
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-green-50 text-green-600">
|
<div className="flex size-12 flex-none items-center justify-center rounded-lg bg-green-50 text-green-600">
|
||||||
<IconMatrix type={item.icon} />
|
<IconMatrix type={item.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue