start of ai-based development
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: elixforms-common-library
|
||||
description: >
|
||||
Documentazione della libreria condivisa common/ del progetto elixForms.
|
||||
Descrive i componenti ElixForms, il pattern di form field factory,
|
||||
le interfacce TypeScript, i tipi, e l'helper per i query parameter.
|
||||
Attiva questa skill quando lavori sui componenti condivisi, crei nuovi tipi di campo,
|
||||
modifichi la gestione dello stato del form, o integri con la piattaforma elixForms.
|
||||
---
|
||||
|
||||
# Libreria Common elixForms
|
||||
|
||||
## Panoramica
|
||||
|
||||
La cartella `react/common/` contiene la libreria condivisa di componenti React/TypeScript usata da tutte le pagine custom. È basata su **Fluent UI React v8** e implementa un sistema di form con campi dinamici.
|
||||
|
||||
## Architettura dei Componenti
|
||||
|
||||
```
|
||||
ElixFormsComponentAbstract (abstract class, extends React.Component)
|
||||
└── ElixFormsComponent (classe concreta, exported as ElixFormsReact)
|
||||
└── Usata nelle pagine come `new ElixForms.ElixFormsReact(props)`
|
||||
```
|
||||
|
||||
### Gerarchia delle classi
|
||||
|
||||
1. **`ElixFormsComponentAbstract`** (`ElixFormsComponentAbstract.tsx`)
|
||||
- Classe astratta che estende `React.Component<IElixFormsComponentProperties, IElixFormsComponentFormState>`
|
||||
- Contiene tutta la logica di creazione dei campi form (factory methods)
|
||||
- Gestisce lo state del form (`formData: { [key: string]: any }`)
|
||||
- Implementa `render()` che genera `<form>` con action POST verso elixForms
|
||||
|
||||
2. **`ElixFormsComponent`** (`ElixFormsComponent.tsx`)
|
||||
- Classe concreta che estende `ElixFormsComponentAbstract`
|
||||
- Esportata come `ElixFormsReact` per uso nelle pagine
|
||||
- Attualmente vuota (la logica è nell'abstract)
|
||||
|
||||
## Interfacce TypeScript
|
||||
|
||||
### `IElixFormsComponentProperties` (Props)
|
||||
```typescript
|
||||
interface IElixFormsComponentProperties {
|
||||
description: string;
|
||||
isDarkTheme: boolean;
|
||||
environmentMessage: string;
|
||||
hasTeamsContext: boolean;
|
||||
userDisplayName: string;
|
||||
additionalFieldsJson?: string; // JSON schema per campi dinamici
|
||||
customFormFieldsConfiguration?: ( // Callback con factory per campi custom
|
||||
formFieldFactory: IElixFormsComponentCustomFormFieldFactory
|
||||
) => JSX.Element;
|
||||
}
|
||||
```
|
||||
|
||||
### `IElixFormsComponentFormState` (State)
|
||||
```typescript
|
||||
interface IElixFormsComponentFormState {
|
||||
formData: { [key: string]: any };
|
||||
}
|
||||
```
|
||||
|
||||
### `IElixFormsComponentCustomFormFieldFactory` (Form Field Factory)
|
||||
```typescript
|
||||
type IElixFormsComponentCustomFormFieldFactory = {
|
||||
createBooleanInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||
createCheckboxInput(name: string, label: string, options: ElixFormsCheckboxOption[]): JSX.Element;
|
||||
createDropdownInput(name: string, label: string, options: ElixFormsDropdownOption[], required?: boolean): JSX.Element;
|
||||
createHiddenInput(name: string): JSX.Element;
|
||||
createNumberInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||
createRadioInput(name: string, label: string, options: ElixFormsRadioOption[], required?: boolean): JSX.Element;
|
||||
createTextInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||
createTextAreaInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||
};
|
||||
```
|
||||
|
||||
### Tipi per le Opzioni (`ElixFormsTypes.tsx`)
|
||||
```typescript
|
||||
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
|
||||
type ElixFormsDropdownOption = { value: number; label: string };
|
||||
type ElixFormsRadioOption = { value: number; label: string };
|
||||
```
|
||||
|
||||
## Form Field Factory Pattern
|
||||
|
||||
Le pagine usano il pattern **Custom Form Field Factory** per dichiarare i campi del form:
|
||||
|
||||
```jsx
|
||||
// In App.jsx della pagina
|
||||
var myElixFormsReact = new ElixForms.ElixFormsReact({
|
||||
// ...props...
|
||||
customFormFieldsConfiguration: (formFieldFactory) => (
|
||||
<>
|
||||
{formFieldFactory.createDropdownInput("COL0015", "Goals", [...options], true)}
|
||||
{formFieldFactory.createTextInput("COL0002", "Campo STRING", true)}
|
||||
{formFieldFactory.createTextAreaInput("COL0003", "Campo TEXTAREA", true)}
|
||||
{formFieldFactory.createBooleanInput("COL0004", "Campo BOOLEAN", true)}
|
||||
{formFieldFactory.createRadioInput("COL0005", "Campo RADIO", [...options], true)}
|
||||
{formFieldFactory.createCheckboxInput("COL0006", "Campo CHECKBOX", [...options])}
|
||||
</>
|
||||
)
|
||||
});
|
||||
```
|
||||
|
||||
### Tipi di Campo Supportati
|
||||
|
||||
| Metodo Factory | Componente FluentUI | HTML Output |
|
||||
|---|---|---|
|
||||
| `createTextInput` | `TextField` | Text input |
|
||||
| `createTextAreaInput` | `TextField` (multiline) | Textarea |
|
||||
| `createNumberInput` | `TextField` (type=number) | Number input |
|
||||
| `createBooleanInput` | `ChoiceGroup` (Sì/No) | Radio buttons |
|
||||
| `createRadioInput` | `ChoiceGroup` | Radio buttons |
|
||||
| `createCheckboxInput` | `Checkbox` + hidden input | Checkboxes |
|
||||
| `createDropdownInput` | `Dropdown` | Select dropdown |
|
||||
| `createHiddenInput` | Native `<input type="text">` | Hidden field |
|
||||
|
||||
## ElixFormsElement (Wrapper Layout)
|
||||
|
||||
`ElixFormsElement` è una classe che incapsula label + input in un layout a due colonne compatibile con il design system elixForms:
|
||||
|
||||
```tsx
|
||||
class ElixFormsElement {
|
||||
constructor(labelElement: ReactElement, inputElement: ReactElement, separator?: string);
|
||||
render(): JSX.Element;
|
||||
}
|
||||
```
|
||||
|
||||
Genera markup con classi CSS specifiche di elixForms:
|
||||
- `.iuFieldContainer` → container del campo
|
||||
- `.attrDisplay_left`, `.attrDisplay_label` → colonna label
|
||||
- `.attrDisplay_right`, `.attrDisplay_input` → colonna input
|
||||
|
||||
## QueryParamHelper
|
||||
|
||||
Utility statica per leggere parametri dalla URL corrente:
|
||||
|
||||
```typescript
|
||||
class QueryParamHelper {
|
||||
static getCheckedFromQuery(paramName: string): number[]; // Per checkbox (valori separati da virgola)
|
||||
static getOptionFromQuery(paramName: string): number | undefined; // Per radio/dropdown
|
||||
static getBooleanFromQuery(paramName: string): boolean | undefined;// Per boolean
|
||||
static getDecodedTextFromQuery(paramName: string): string | undefined; // Per testo (URI decoded)
|
||||
}
|
||||
```
|
||||
|
||||
Usato per pre-popolare i campi form con i valori passati via query string dalla piattaforma elixForms.
|
||||
|
||||
## Campi Obbligatori elixForms
|
||||
|
||||
Il componente abstract gestisce automaticamente questi parametri come hidden inputs:
|
||||
- `RWE2_MODULE_ID`
|
||||
- `RWE2_REQUEST_ID`
|
||||
- `custom-workflow-back-url`
|
||||
- `custom-workflow-generic-id`
|
||||
- `custom-workflow-current-tabrel-genid`
|
||||
- `custom-workflow-source-field`
|
||||
- `crc`
|
||||
- `MODULE_TESTMODE_KEY`
|
||||
- `ELANG`
|
||||
|
||||
## Note Importanti
|
||||
|
||||
- Lo **state management** attuale usa `React.Component` class-based con `this.state` e `this.setState`
|
||||
- I checkbox gestiscono un `Map<string, number[]>` interno per tracciare i valori selezionati
|
||||
- Il form fa POST a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
|
||||
- L'encoding charset è `ISO-8859-1` per compatibilità con il backend
|
||||
- La libreria usa **SCSS modules** per gli stili (`ElixFormsComponent.module.scss`)
|
||||
- Il package `common` è di tipo `commonjs` (diverso dalle pagine che sono `module`)
|
||||
@@ -0,0 +1,229 @@
|
||||
---
|
||||
name: elixforms-create-new-page
|
||||
description: >
|
||||
Guida step-by-step per creare una nuova pagina custom nel progetto elixForms.
|
||||
Copre la struttura dei file, la configurazione Vite per single-bundle,
|
||||
il setup TypeScript, l'importazione dei componenti common, e le convenzioni.
|
||||
Attiva questa skill quando devi creare una nuova pagina, duplicare una pagina esistente,
|
||||
o scaffoldare un nuovo micro-progetto nel monorepo.
|
||||
---
|
||||
|
||||
# Creare una Nuova Pagina elixForms Custom
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Node.js installato
|
||||
- Dipendenze root installate: `cd react && npm install`
|
||||
|
||||
## Step 1: Scaffolding con Vite
|
||||
|
||||
```powershell
|
||||
cd react
|
||||
npm create vite@latest <nome-pagina> -- --template react
|
||||
cd <nome-pagina>
|
||||
npm install vite-plugin-css-injected-by-js
|
||||
npm install -D sass-embedded
|
||||
```
|
||||
|
||||
## Step 2: Configurare package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@elixforms/<nome-pagina>",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vite-plugin-css-injected-by-js": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"sass-embedded": "^1.99.0",
|
||||
"vite": "^8.0.10"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Nota**: React, ReactDOM e FluentUI NON vanno nel package.json della pagina.
|
||||
> Sono nel `react/package.json` root e vengono risolti via hoisting di npm.
|
||||
|
||||
## Step 3: Configurare vite.config.js
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
cssInjectedByJsPlugin()
|
||||
],
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
input: 'src/main.jsx', // o main.tsx se TypeScript
|
||||
output: {
|
||||
codeSplitting: false,
|
||||
manualChunks: undefined,
|
||||
entryFileNames: '<nome-pagina>.js',
|
||||
assetFileNames: '[name].[ext]'
|
||||
},
|
||||
},
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@common": path.resolve(__dirname, "../common"),
|
||||
}
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
allow: [".."]
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Step 4: Configurare tsconfig.json
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"references": [
|
||||
{ "path": "../common" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"allowJs": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
```
|
||||
|
||||
## Step 5: Creare il file di dichiarazioni globali
|
||||
|
||||
`src/global.d.ts`:
|
||||
```typescript
|
||||
declare module "*.css";
|
||||
declare module "*.svg";
|
||||
declare module "*.png";
|
||||
```
|
||||
|
||||
## Step 6: Creare l'entry point
|
||||
|
||||
`src/main.jsx` (o `main.tsx`):
|
||||
```jsx
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
if (!rootElement) throw new Error('Root element not found')
|
||||
|
||||
createRoot(rootElement).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
```
|
||||
|
||||
## Step 7: Creare il componente App
|
||||
|
||||
`src/App.jsx` (o `App.tsx`):
|
||||
```jsx
|
||||
import './App.css'
|
||||
import * as ElixForms from '@common/src/ElixFormsComponent';
|
||||
|
||||
function App() {
|
||||
var myElixFormsReact = new ElixForms.ElixFormsReact({
|
||||
description: "Descrizione pagina",
|
||||
isDarkTheme: false,
|
||||
environmentMessage: "",
|
||||
hasTeamsContext: false,
|
||||
userDisplayName: "",
|
||||
customFormFieldsConfiguration: (formFieldFactory) => (
|
||||
<>
|
||||
{/* Aggiungi i tuoi campi qui usando formFieldFactory */}
|
||||
</>
|
||||
)
|
||||
});
|
||||
|
||||
var elixFormsReactRender = myElixFormsReact.render();
|
||||
|
||||
return <>
|
||||
<div className="container_12" id="pageBody">
|
||||
<div className="grid_12">
|
||||
<div id="userConsole" className="fe-behaviour">
|
||||
{/* Header, contenuto e footer della pagina */}
|
||||
<div className="grid_12 console_main">
|
||||
<div className="inside">
|
||||
<div className="receipt">
|
||||
<div className="container">
|
||||
<h1>Titolo Pagina</h1>
|
||||
{elixFormsReactRender}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
## Step 8: Configurare i CSS
|
||||
|
||||
`src/App.css` — Importa i CSS remoti del design system elixForms:
|
||||
```css
|
||||
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
|
||||
/* ... altri CSS necessari */
|
||||
```
|
||||
|
||||
## Step 9: Configurare VS Code (opzionale)
|
||||
|
||||
Aggiungere configurazione di launch e task in `.vscode/launch.json` e `.vscode/tasks.json` seguendo il pattern esistente per `scelta-carriera`.
|
||||
|
||||
## Step 10: Build e Deploy
|
||||
|
||||
```powershell
|
||||
npm run build
|
||||
```
|
||||
|
||||
Output in `dist/`:
|
||||
- `<nome-pagina>.js` — Singolo bundle JS pronto per il deploy
|
||||
- Eventuali asset (immagini) referenziati
|
||||
|
||||
## Checklist Nuova Pagina
|
||||
|
||||
- [ ] Cartella creata in `react/<nome-pagina>/`
|
||||
- [ ] `package.json` con `"name": "@elixforms/<nome-pagina>"`
|
||||
- [ ] `vite.config.js` con `entryFileNames: '<nome-pagina>.js'`
|
||||
- [ ] `tsconfig.json` estende `../tsconfig.base.json`
|
||||
- [ ] Alias `@common` configurato in vite.config.js
|
||||
- [ ] CSS remoti importati in App.css
|
||||
- [ ] Entry point `main.jsx` con mount su `#root`
|
||||
- [ ] `global.d.ts` con dichiarazioni per moduli CSS/SVG/PNG
|
||||
- [ ] `npm install` eseguito nella cartella della pagina
|
||||
- [ ] `npm run dev` funziona senza errori
|
||||
- [ ] `npm run build` produce singolo bundle in `dist/`
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
name: elixforms-css-design-system
|
||||
description: >
|
||||
Strategia CSS e design system del progetto elixForms.
|
||||
Documenta come vengono importati i CSS remoti della piattaforma,
|
||||
il layout a griglia elixForms, le classi CSS specifiche della piattaforma,
|
||||
gli stili SCSS modules nella libreria common, e la struttura visiva delle pagine.
|
||||
Attiva questa skill quando lavori sugli stili, sul layout delle pagine,
|
||||
o devi capire come funzionano le classi CSS del design system elixForms.
|
||||
---
|
||||
|
||||
# CSS e Design System elixForms
|
||||
|
||||
## Strategia CSS
|
||||
|
||||
Il progetto utilizza **tre livelli di stili**:
|
||||
|
||||
1. **CSS remoti** — Design system dell'Università di Parma, caricati via `@import url(...)` da `console-unipr.elixforms.it`
|
||||
2. **SCSS Modules** — Stili specifici dei componenti in `common/` (es. `ElixFormsComponent.module.scss`)
|
||||
3. **CSS locali** — Stili specifici della pagina (es. `App.css`, `index.css`)
|
||||
|
||||
## CSS Remoti (Design System UniPR/elixForms)
|
||||
|
||||
Importati in `App.css` di ogni pagina:
|
||||
|
||||
```css
|
||||
/* CSS principali del design system */
|
||||
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_overrides.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/isipcss_overrides.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/iulib.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/print.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/responsive.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/util.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/plugins/modaldisplay.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/css/themes/blu-italia.css');
|
||||
@import url('https://console-unipr.elixforms.it/rwe2/isipcss/css/grid_fluid.css');
|
||||
```
|
||||
|
||||
> **IMPORTANTE**: Questi CSS sono caricati a runtime dal CDN e **non vengono bundlati** nel JS.
|
||||
> La pagina deve avere connessione internet per visualizzarsi correttamente.
|
||||
|
||||
## Layout della Pagina (Struttura HTML)
|
||||
|
||||
Le pagine seguono un layout standard elixForms con classi CSS specifiche:
|
||||
|
||||
```html
|
||||
<div class="container_12" id="pageBody">
|
||||
<div class="grid_12">
|
||||
<div id="userConsole" class="fe-behaviour">
|
||||
|
||||
<!-- HEADER -->
|
||||
<header>
|
||||
<div class="neutral">
|
||||
<div class="grid_12">
|
||||
<div class="console_header">
|
||||
<div class="leftDiv">
|
||||
<h1>elixForms</h1>
|
||||
<div class="logo"><!-- Logo UniPR --></div>
|
||||
</div>
|
||||
<div class="rightDiv">...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- CONTENUTO PRINCIPALE -->
|
||||
<div class="grid_12 console_main">
|
||||
<div class="inside">
|
||||
<div class="fe-module-header-container">
|
||||
<h2>Titolo Modulo</h2>
|
||||
</div>
|
||||
<div class="receipt">
|
||||
<div class="container">
|
||||
<!-- Contenuto custom della pagina -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="it-footer">
|
||||
<div class="it-footer-main">
|
||||
<div class="container">
|
||||
<div class="subfooter-top">powered by elixForms</div>
|
||||
<div class="subfooter-bottom">versione X.XX.X</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Classi CSS del Design System
|
||||
|
||||
### Layout a Griglia
|
||||
| Classe | Scopo |
|
||||
|---|---|
|
||||
| `container_12` | Container principale a 12 colonne |
|
||||
| `grid_12` | Occupa tutte le 12 colonne |
|
||||
| `clear` | Clearfix |
|
||||
|
||||
### Struttura Pagina
|
||||
| Classe | Scopo |
|
||||
|---|---|
|
||||
| `fe-behaviour` | Container principale della console utente |
|
||||
| `console_header` | Header della console |
|
||||
| `console_main` | Area contenuto principale |
|
||||
| `fe-module-header-container` | Header del modulo/form |
|
||||
| `receipt` | Container del contenuto del form |
|
||||
|
||||
### Layout Campi Form (ElixFormsElement)
|
||||
| Classe | Scopo |
|
||||
|---|---|
|
||||
| `iuFieldContainer` | Container di un campo form (label + input) |
|
||||
| `attrDisplay_left` | Colonna sinistra (label) |
|
||||
| `attrDisplay_right` | Colonna destra (input) |
|
||||
| `attrDisplay_label` | Marcatore per la label |
|
||||
| `attrDisplay_input` | Marcatore per l'input |
|
||||
| `iuLabelContainer` | Container interno della label |
|
||||
| `iuInputContainer` | Container interno dell'input |
|
||||
| `iuTypeString` | Tipo di input stringa |
|
||||
| `iuClearContainer` | Clearfix interno |
|
||||
|
||||
### Classi Input FluentUI Custom
|
||||
| Classe | Componente |
|
||||
|---|---|
|
||||
| `isiportalPartialAdminFormFieldSingleLineText` | TextField singola riga |
|
||||
| `isiportalPartialAdminFormFieldMultiLineText` | TextField multiriga |
|
||||
| `isiportalPartialAdminFormFieldRadio` | ChoiceGroup radio |
|
||||
| `isiportalPartialAdminFormFieldSelect` | Dropdown select |
|
||||
| `isiportalPartialAdminCheckboxFieldItem` | Checkbox singolo |
|
||||
|
||||
## SCSS Modules (common/)
|
||||
|
||||
`ElixFormsComponent.module.scss`:
|
||||
```scss
|
||||
@import '~@fluentui/react/dist/sass/References.scss';
|
||||
|
||||
.elixFormsComponent {
|
||||
overflow: hidden;
|
||||
padding: 1em;
|
||||
color: "[theme:bodyText, default: #323130]";
|
||||
color: var(--bodyText);
|
||||
&.teams {
|
||||
font-family: $ms-font-family-fallbacks;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Nota**: Gli stili usano il **theming di FluentUI** con variabili CSS e placeholder di tema SharePoint.
|
||||
|
||||
## Logo e Risorse Esterne
|
||||
|
||||
- **Logo UniPR**: `https://console-unipr.elixforms.it/elixFormsCustom/images/logo.png`
|
||||
- Le immagini locali vanno in `src/assets/` e importate come moduli ES
|
||||
- Le immagini vengono copiate nella `dist/` durante la build
|
||||
|
||||
## Note Importanti
|
||||
|
||||
1. Il CSS di `index.css` nella pagina `scelta-carriera` è **attualmente tutto commentato** — si usano solo i CSS remoti
|
||||
2. Il CSS injection plugin (`vite-plugin-css-injected-by-js`) gestisce solo i CSS locali e SCSS, non gli `@import url(...)` che restano come riferimenti esterni
|
||||
3. Per funzionare correttamente, la pagina deployata deve poter raggiungere `console-unipr.elixforms.it`
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
name: elixforms-dev-workflow
|
||||
description: >
|
||||
Workflow di sviluppo, debug e deploy del progetto elixForms Custom Pages.
|
||||
Copre i comandi per avviare il dev server, buildare, debuggare con VS Code,
|
||||
e le configurazioni di launch/tasks.
|
||||
Attiva questa skill quando devi eseguire comandi di sviluppo,
|
||||
configurare il debug, o gestire il ciclo dev/build/deploy.
|
||||
---
|
||||
|
||||
# Workflow di Sviluppo
|
||||
|
||||
## Comandi Principali
|
||||
|
||||
Tutti i comandi vanno eseguiti **dalla cartella della pagina** (es. `react/scelta-carriera/`):
|
||||
|
||||
```powershell
|
||||
# Installare le dipendenze (prima volta o dopo modifiche a package.json)
|
||||
cd react
|
||||
npm install
|
||||
cd scelta-carriera
|
||||
npm install
|
||||
|
||||
# Avviare il dev server con HMR
|
||||
npm run dev
|
||||
|
||||
# Build per produzione (singolo bundle JS)
|
||||
npm run build
|
||||
|
||||
# Preview della build di produzione
|
||||
npm run preview
|
||||
|
||||
# Lint del codice
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## Configurazione VS Code
|
||||
|
||||
### Launch Configurations (`.vscode/launch.json`)
|
||||
|
||||
#### 1. "Scelta Carriere (BROWSER)" — Dev + Chrome Debug
|
||||
- Avvia `npm run dev` nella cartella della pagina
|
||||
- Apre automaticamente Chrome quando il server è pronto
|
||||
- Pattern di rilevamento: `Local:\s+http://localhost:([0-9]+)/`
|
||||
- WebRoot per sourcemaps: `${workspaceFolder}/react/scelta-carriera/src`
|
||||
|
||||
#### 2. "Scelta Carriere (DEBUG ONLY)" — Solo Dev Server
|
||||
- Avvia `npm run dev` senza aprire il browser
|
||||
- Utile per debug da terminale o browser già aperto
|
||||
|
||||
### Variabili d'Ambiente
|
||||
- `NO_COLOR: "1"` — Disabilita colori ANSI nell'output (migliora leggibilità nel debug console)
|
||||
|
||||
### Skip Files nel Debug
|
||||
```json
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/**",
|
||||
"**/@vite/client/**"
|
||||
]
|
||||
```
|
||||
|
||||
### Tasks (`.vscode/tasks.json`)
|
||||
|
||||
#### "Start scelta-carriera (Vite App)"
|
||||
- Task background che avvia `npm run dev`
|
||||
- Problem matcher configurato per output Vite:
|
||||
- `beginsPattern`: "VITE"
|
||||
- `endsPattern`: "ready in"
|
||||
|
||||
## Aggiungere una Nuova Pagina al Debug
|
||||
|
||||
Per aggiungere una nuova configurazione di debug per una pagina:
|
||||
|
||||
```json
|
||||
// In .vscode/launch.json, aggiungere in "configurations":
|
||||
{
|
||||
"name": "<Nome Pagina> (BROWSER)",
|
||||
"cwd": "${workspaceFolder}/react/<nome-pagina>",
|
||||
"env": { "NO_COLOR": "1" },
|
||||
"outputCapture": "console",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev"],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/**",
|
||||
"**/@vite/client/**"
|
||||
],
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithChrome",
|
||||
"pattern": "Local:\\s+http://localhost:([0-9]+)/",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"webRoot": "${workspaceFolder}/react/<nome-pagina>/src"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Flusso di Deploy
|
||||
|
||||
1. `npm run build` nella cartella della pagina
|
||||
2. Output: `dist/<nome-pagina>.js` (+ sourcemap e asset)
|
||||
3. Caricare il file JS sul server remoto `console-unipr.elixforms.it`
|
||||
4. La pagina viene servita dalla piattaforma elixForms con i parametri query appropriati
|
||||
|
||||
## Testing Locale con Query Parameters
|
||||
|
||||
Per testare localmente con i parametri elixForms, aggiungere query params all'URL del dev server:
|
||||
|
||||
```
|
||||
http://localhost:5173/?RWE2_MODULE_ID=123&RWE2_REQUEST_ID=456&COL0002=test&COL0015=3
|
||||
```
|
||||
|
||||
I valori verranno letti da `QueryParamHelper` e pre-popoleranno i campi del form.
|
||||
|
||||
## Struttura delle Dipendenze
|
||||
|
||||
```
|
||||
react/ ← npm install qui per dipendenze condivise
|
||||
├── node_modules/ ← react, react-dom, @fluentui/react
|
||||
├── package.json
|
||||
└── scelta-carriera/ ← npm install qui per devDependencies
|
||||
├── node_modules/ ← vite, eslint, plugin, sass-embedded
|
||||
└── package.json
|
||||
```
|
||||
|
||||
> **Nota**: Le dipendenze runtime (React, FluentUI) sono nel `package.json` root.
|
||||
> Le dipendenze di sviluppo (Vite, ESLint) sono nel `package.json` della pagina.
|
||||
> npm risolve le dipendenze risalendo la gerarchia delle cartelle (hoisting).
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
name: elixforms-project-architecture
|
||||
description: >
|
||||
Architettura e struttura del progetto elixForms Custom Pages.
|
||||
Descrive il monorepo, la libreria common condivisa, il pattern delle pagine autonome
|
||||
e tutte le convenzioni di naming, configurazione e build.
|
||||
Attiva questa skill quando lavori su qualsiasi parte del progetto,
|
||||
crei nuove pagine, modifichi l'architettura o devi capire come è organizzato il codice.
|
||||
---
|
||||
|
||||
# Architettura del Progetto elixForms Custom Pages
|
||||
|
||||
## Panoramica
|
||||
|
||||
Questo progetto è un **monorepo** per la creazione di pagine web React custom destinate alla piattaforma **elixForms** dell'Università di Parma (UniPR). Ogni pagina è un micro-progetto indipendente che produce un **singolo file JS bundle** contenente React, Fluent UI, CSS e il codice custom, da pubblicare su un server remoto.
|
||||
|
||||
## Struttura del Workspace
|
||||
|
||||
```
|
||||
elixforms-custom-pages/ ← Root del workspace (VS Code)
|
||||
├── .vscode/ ← Configurazioni VS Code (launch, tasks)
|
||||
├── docs/ ← Documentazione (PDF manuali)
|
||||
└── react/ ← Root del monorepo React
|
||||
├── package.json ← Dipendenze condivise (React, ReactDOM, FluentUI)
|
||||
├── tsconfig.base.json ← Configurazione TypeScript base condivisa
|
||||
├── .gitignore
|
||||
├── common/ ← Libreria condivisa di componenti ElixForms
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json ← Estende tsconfig.base, composite: true
|
||||
│ └── src/
|
||||
│ ├── ElixFormsComponent.tsx
|
||||
│ ├── ElixFormsComponentAbstract.tsx
|
||||
│ ├── ElixFormsElement.tsx
|
||||
│ ├── ElixFormsTypes.tsx
|
||||
│ ├── ElixFormsComponent.module.scss
|
||||
│ ├── IElixFormsComponentProperties.ts
|
||||
│ ├── IElixFormsComponentFormState.tsx
|
||||
│ ├── IElixFormsComponentCustomFormFieldFactory.ts
|
||||
│ └── QueryParamHelper.tsx
|
||||
└── scelta-carriera/ ← Esempio di pagina custom (micro-progetto)
|
||||
├── package.json
|
||||
├── tsconfig.json ← Estende tsconfig.base
|
||||
├── vite.config.js
|
||||
├── eslint.config.js
|
||||
├── index.html
|
||||
└── src/
|
||||
├── main.jsx ← Entry point React
|
||||
├── App.jsx ← Componente principale della pagina
|
||||
├── App.css ← CSS (importa CSS remoti di elixForms)
|
||||
├── index.css ← CSS globale (attualmente commentato)
|
||||
├── global.d.ts ← Dichiarazioni di tipo per moduli
|
||||
└── assets/ ← Immagini e risorse statiche
|
||||
```
|
||||
|
||||
## Pattern Architetturale: Micro-Progetto per Pagina
|
||||
|
||||
Ogni pagina (es. `scelta-carriera/`) è un progetto Vite completamente autonomo che:
|
||||
|
||||
1. **Importa dalla libreria `common/`** tramite alias `@common` configurato in `vite.config.js`
|
||||
2. **Ha le proprie dipendenze** nel suo `package.json` (solo devDependencies e plugin Vite)
|
||||
3. **Condivide le dipendenze runtime** (React, ReactDOM, FluentUI) dal `package.json` root di `react/`
|
||||
4. **Produce un singolo file JS** tramite la configurazione Rollup in `vite.config.js`
|
||||
5. **Estende il tsconfig base** per avere configurazione TypeScript coerente
|
||||
|
||||
## Dipendenze Condivise (react/package.json)
|
||||
|
||||
Le dipendenze runtime sono hoisted al livello `react/`:
|
||||
- `react` ^19.2.7
|
||||
- `react-dom` ^19.2.5
|
||||
- `@fluentui/react` ^8.125.6
|
||||
|
||||
## Configurazione TypeScript
|
||||
|
||||
### Base (tsconfig.base.json)
|
||||
- `module`: ESNext
|
||||
- `target`: ES2022
|
||||
- `moduleResolution`: bundler
|
||||
- `jsx`: react-jsx
|
||||
- `strict`: true
|
||||
- Path alias: `@common/*` → `./common/*`
|
||||
|
||||
### Common (common/tsconfig.json)
|
||||
- `composite`: true (per project references)
|
||||
- `declaration`: true, `declarationMap`: true
|
||||
|
||||
### Pagine (scelta-carriera/tsconfig.json)
|
||||
- `extends`: `../tsconfig.base.json`
|
||||
- `references`: `../common`
|
||||
- `allowJs`: true, `checkJs`: true (supporto misto JSX/TSX)
|
||||
|
||||
## Configurazione Build (Vite)
|
||||
|
||||
Ogni pagina usa questa configurazione per generare un **singolo bundle JS**:
|
||||
|
||||
```js
|
||||
// Plugin chiave
|
||||
react() // Supporto React JSX
|
||||
cssInjectedByJsPlugin() // CSS iniettato nel JS (no file .css separati)
|
||||
|
||||
// Build options
|
||||
cssCodeSplit: false
|
||||
rollupOptions.input: 'src/main.jsx'
|
||||
rollupOptions.output:
|
||||
codeSplitting: false
|
||||
manualChunks: undefined
|
||||
entryFileNames: '<nome-pagina>.js' // Nome descrittivo (es. 'scelta-carriera.js')
|
||||
assetFileNames: '[name].[ext]'
|
||||
sourcemap: true
|
||||
```
|
||||
|
||||
## Resolve Alias
|
||||
|
||||
```js
|
||||
resolve.alias: {
|
||||
"@common": path.resolve(__dirname, "../common")
|
||||
}
|
||||
server.fs.allow: [".."] // Permette import da common/
|
||||
```
|
||||
|
||||
## CSS Strategy
|
||||
|
||||
Le pagine caricano CSS dal server remoto elixForms tramite `@import url(...)` in `App.css`:
|
||||
- CSS da `console-unipr.elixforms.it` per il design system dell'università
|
||||
- I componenti FluentUI usano stili inline/theme nativi
|
||||
- SCSS modules sono usati in `common/` per stili dei componenti condivisi
|
||||
- `sass-embedded` è installato come devDependency per supporto SCSS
|
||||
|
||||
## Integrazione con elixForms
|
||||
|
||||
La pagina custom interagisce con la piattaforma elixForms tramite:
|
||||
1. **Query parameters**: Parametri obbligatori passati nell'URL (es. `RWE2_MODULE_ID`, `RWE2_REQUEST_ID`, `crc`, etc.)
|
||||
2. **Form POST**: Il form fa submit a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
|
||||
3. **Hidden inputs**: I parametri query vengono inseriti come campi hidden nel form
|
||||
4. **Encoding**: `acceptCharset="ISO-8859-1"` per compatibilità con il backend
|
||||
|
||||
## Convenzioni di Naming
|
||||
|
||||
- **Cartelle pagina**: kebab-case (es. `scelta-carriera`)
|
||||
- **Package name**: `@elixforms/<nome-pagina>` (es. `@elixforms/scelta-carriera`)
|
||||
- **Bundle output**: `<nome-pagina>.js` (es. `scelta-carriera.js`)
|
||||
- **Componenti React**: PascalCase
|
||||
- **File sorgente**: I file correnti sono `.jsx` ma il progetto ha pieno supporto TypeScript (`.tsx`)
|
||||
- **Tipo moduli**: `"type": "module"` nelle pagine
|
||||
|
||||
## Ambiente di Sviluppo
|
||||
|
||||
- **Dev server**: `npm run dev` (Vite HMR)
|
||||
- **Build**: `npm run build` → output in `dist/`
|
||||
- **Debug VS Code**: Configurazioni in `.vscode/launch.json` per avviare Vite + Chrome
|
||||
- **Lint**: ESLint con plugin react-hooks e react-refresh
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
name: elixforms-typescript-conventions
|
||||
description: >
|
||||
Convenzioni TypeScript e React per il progetto elixForms.
|
||||
Copre la configurazione TypeScript, i pattern di codice, la gestione dei tipi,
|
||||
l'uso di JSDoc per type annotations nei file .jsx, e le best practices.
|
||||
Attiva questa skill quando scrivi codice TypeScript o React,
|
||||
crei nuovi componenti, definisci interfacce, o gestisci i tipi nel progetto.
|
||||
---
|
||||
|
||||
# Convenzioni TypeScript e React
|
||||
|
||||
## Configurazione TypeScript del Progetto
|
||||
|
||||
### Opzioni Compiler Principali
|
||||
- **target**: ES2022
|
||||
- **module**: ESNext
|
||||
- **moduleResolution**: bundler
|
||||
- **jsx**: react-jsx (trasformazione automatica JSX senza import React)
|
||||
- **strict**: true
|
||||
- **isolatedModules**: true
|
||||
- **verbatimModuleSyntax**: true (usa `import type` per import solo di tipi)
|
||||
|
||||
### File Misti JSX/TSX
|
||||
|
||||
Il progetto supporta **sia file `.jsx` che `.tsx`** grazie a:
|
||||
- `allowJs: true` e `checkJs: true` nel tsconfig delle pagine
|
||||
- Type annotations via JSDoc nei file `.jsx`
|
||||
|
||||
#### Pattern JSDoc per Tipi nei File .jsx
|
||||
|
||||
```jsx
|
||||
/** @type {import('@common/src/ElixFormsComponent').ElixFormsReact} */
|
||||
var myElixFormsReact = new ElixForms.ElixFormsReact({...});
|
||||
|
||||
// Per i parametri di callback
|
||||
customFormFieldsConfiguration: (
|
||||
/** @type {import('../../common/src/IElixFormsComponentCustomFormFieldFactory').IElixFormsComponentCustomFormFieldFactory} */
|
||||
formFieldFactory) => (...)
|
||||
```
|
||||
|
||||
## Pattern React Correnti
|
||||
|
||||
### Class Components (common/)
|
||||
|
||||
La libreria `common/` usa **React Class Components** (non hooks):
|
||||
|
||||
```tsx
|
||||
abstract class ElixFormsComponentAbstract
|
||||
extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
|
||||
|
||||
constructor(props: IElixFormsComponentProperties) {
|
||||
super(props);
|
||||
this.state = { formData: {} };
|
||||
}
|
||||
|
||||
// State gestito con this.setState()
|
||||
this.setState({ formData: { ...this.state.formData, [name]: value } });
|
||||
}
|
||||
```
|
||||
|
||||
### Functional Components (pagine)
|
||||
|
||||
Le pagine usano **Functional Components** con hooks:
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Istanziazione del Componente ElixForms
|
||||
|
||||
Il componente ElixForms viene istanziato come classe (non come JSX):
|
||||
|
||||
```jsx
|
||||
// ✅ Corretto - istanziazione diretta
|
||||
var myElixFormsReact = new ElixForms.ElixFormsReact({...props...});
|
||||
var elixFormsReactRender = myElixFormsReact.render();
|
||||
|
||||
// Poi nel JSX
|
||||
return <>{elixFormsReactRender}</>;
|
||||
|
||||
// ❌ NON usare come componente JSX
|
||||
return <ElixForms.ElixFormsReact {...props} />;
|
||||
```
|
||||
|
||||
## Import Conventions
|
||||
|
||||
### Import Type-Only (verbatimModuleSyntax)
|
||||
|
||||
Con `verbatimModuleSyntax: true`, usare sempre `import type` per i tipi:
|
||||
|
||||
```tsx
|
||||
// ✅ Corretto
|
||||
import type { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
|
||||
import type { JSX } from 'react';
|
||||
|
||||
// ❌ Errato - causerà errore con verbatimModuleSyntax
|
||||
import { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
|
||||
```
|
||||
|
||||
### Import di Moduli
|
||||
|
||||
```tsx
|
||||
// Fluent UI - import tutto il namespace
|
||||
import * as FluentUI from '@fluentui/react';
|
||||
|
||||
// React - import specifici
|
||||
import { Component, type FormEvent, type JSX, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
// Componenti common - import namespace
|
||||
import * as ElixForms from '@common/src/ElixFormsComponent';
|
||||
|
||||
// CSS
|
||||
import './App.css'
|
||||
```
|
||||
|
||||
## Tipi Custom del Progetto
|
||||
|
||||
```typescript
|
||||
// Opzioni per campi form
|
||||
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
|
||||
type ElixFormsDropdownOption = { value: number; label: string };
|
||||
type ElixFormsRadioOption = { value: number; label: string };
|
||||
|
||||
// State del form
|
||||
interface IElixFormsComponentFormState {
|
||||
formData: { [key: string]: any };
|
||||
}
|
||||
```
|
||||
|
||||
## Dichiarazioni di Modulo
|
||||
|
||||
`global.d.ts` nella cartella `src/` di ogni pagina:
|
||||
|
||||
```typescript
|
||||
declare module "*.css";
|
||||
declare module "*.svg";
|
||||
declare module "*.png";
|
||||
```
|
||||
|
||||
## Best Practices per il Progetto
|
||||
|
||||
1. **Naming**:
|
||||
- Interfacce: prefisso `I` (es. `IElixFormsComponentProperties`)
|
||||
- Tipi: PascalCase (es. `ElixFormsCheckboxOption`)
|
||||
- File interfacce: nome dell'interfaccia (es. `IElixFormsComponentProperties.ts`)
|
||||
- File componenti: nome del componente (es. `ElixFormsComponent.tsx`)
|
||||
|
||||
2. **Estensioni file**:
|
||||
- `.tsx` per componenti React con TypeScript (common/)
|
||||
- `.ts` per moduli TypeScript puri (interfacce, tipi)
|
||||
- `.jsx` per componenti React con JavaScript (pagine attuali)
|
||||
- `.scss` per stili con moduli SCSS
|
||||
|
||||
3. **Export**:
|
||||
- Named export per tipi e interfacce
|
||||
- Default export per classi componente
|
||||
- Re-export con alias: `export { ElixFormsComponent as ElixFormsReact }`
|
||||
|
||||
4. **State management**:
|
||||
- Usare `this.setState` con spread operator per update immutabili
|
||||
- I checkbox mantengono un `Map<string, number[]>` separato
|
||||
|
||||
5. **Fluent UI**:
|
||||
- Usare Fluent UI React v8 (`@fluentui/react`)
|
||||
- Componenti: `TextField`, `Checkbox`, `ChoiceGroup`, `Dropdown`, `Label`, `Stack`
|
||||
- Le opzioni radio usano `IChoiceGroupOption[]`
|
||||
- Le opzioni dropdown usano `IDropdownOption[]`
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
name: elixforms-vite-build-config
|
||||
description: >
|
||||
Configurazione Vite e Rollup per il progetto elixForms.
|
||||
Copre la build single-bundle, i plugin utilizzati, gli alias,
|
||||
il sourcemap, e la strategia CSS (injection nel JS).
|
||||
Attiva questa skill quando devi modificare la build configuration,
|
||||
risolvere problemi di bundling, aggiungere plugin Vite,
|
||||
o ottimizzare l'output di produzione.
|
||||
---
|
||||
|
||||
# Configurazione Vite e Build
|
||||
|
||||
## Stack di Build
|
||||
|
||||
- **Vite** v8.x — Dev server e bundler
|
||||
- **@vitejs/plugin-react** v6.x — Supporto React/JSX
|
||||
- **vite-plugin-css-injected-by-js** v5.x — Inietta CSS nel bundle JS
|
||||
- **sass-embedded** v1.99.x — Compilazione SCSS (per common/)
|
||||
- **Rollup** — Bundler interno di Vite (configurato via `rollupOptions`)
|
||||
|
||||
## Configurazione Vite Completa di Riferimento
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
cssInjectedByJsPlugin()
|
||||
],
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
input: 'src/main.jsx',
|
||||
output: {
|
||||
codeSplitting: false,
|
||||
manualChunks: undefined,
|
||||
entryFileNames: '<nome-pagina>.js',
|
||||
assetFileNames: '[name].[ext]'
|
||||
},
|
||||
},
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@common": path.resolve(__dirname, "../common"),
|
||||
}
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
allow: [".."]
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Dettaglio Opzioni Critiche
|
||||
|
||||
### Single Bundle (Rollup)
|
||||
| Opzione | Valore | Scopo |
|
||||
|---|---|---|
|
||||
| `codeSplitting` | `false` | Disabilita il code splitting |
|
||||
| `manualChunks` | `undefined` | Impedisce separazione vendor/app |
|
||||
| `entryFileNames` | `'<nome>.js'` | Nome deterministico senza hash |
|
||||
| `assetFileNames` | `'[name].[ext]'` | Asset senza hash nei nomi |
|
||||
|
||||
### CSS Strategy
|
||||
| Opzione | Valore | Scopo |
|
||||
|---|---|---|
|
||||
| `cssCodeSplit` | `false` | No file CSS separati |
|
||||
| `cssInjectedByJsPlugin()` | plugin | CSS iniettato runtime nel DOM via JS |
|
||||
|
||||
### Alias di Import
|
||||
| Alias | Path | Scopo |
|
||||
|---|---|---|
|
||||
| `@common` | `../common` | Accesso alla libreria condivisa |
|
||||
|
||||
### Dev Server
|
||||
| Opzione | Valore | Scopo |
|
||||
|---|---|---|
|
||||
| `server.fs.allow` | `[".."]` | Permette import fuori dalla root del progetto |
|
||||
|
||||
## Comandi
|
||||
|
||||
```powershell
|
||||
# Dev con HMR
|
||||
npm run dev
|
||||
|
||||
# Build produzione
|
||||
npm run build
|
||||
|
||||
# Preview build produzione
|
||||
npm run preview
|
||||
|
||||
# Lint
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## Output della Build
|
||||
|
||||
```
|
||||
dist/
|
||||
<nome-pagina>.js ← Bundle unico (React + FluentUI + CSS + codice)
|
||||
<nome-pagina>.js.map ← Source map
|
||||
*.png, *.svg ← Asset referenziati nel codice
|
||||
```
|
||||
|
||||
## Risoluzione Problemi Comuni
|
||||
|
||||
### Errore: "The request url is outside of Vite serving allow list"
|
||||
- **Causa**: Import di file dalla cartella `common/` o altre cartelle esterne
|
||||
- **Fix**: Aggiungere `server.fs.allow: [".."]` nella configurazione Vite
|
||||
|
||||
### Errore: "Cannot find module '@common/...'"
|
||||
- **Causa**: Alias non configurato o path errato
|
||||
- **Fix**: Verificare `resolve.alias` e che la cartella `common/` esista
|
||||
|
||||
### Bundle troppo grande
|
||||
- **Causa**: FluentUI include molti componenti
|
||||
- **Fix possibili**:
|
||||
- Tree shaking (import specifici: `import { TextField } from '@fluentui/react'`)
|
||||
- Considerare `@fluentui/react-components` (v9) per bundle più piccoli
|
||||
- Esternalizzare React se la pagina host lo fornisce già
|
||||
|
||||
### CSS non applicato in produzione
|
||||
- **Causa**: `cssInjectedByJsPlugin` non installato o non configurato
|
||||
- **Fix**: Verificare plugin in `vite.config.js` e `npm install vite-plugin-css-injected-by-js`
|
||||
|
||||
### SCSS non compilato
|
||||
- **Causa**: `sass-embedded` mancante
|
||||
- **Fix**: `npm install -D sass-embedded`
|
||||
Binary file not shown.
Generated
+3
-2
@@ -262,14 +262,14 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.7",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -279,6 +279,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
|
||||
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
|
||||
Generated
+42
-24
@@ -54,6 +54,7 @@
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@@ -270,31 +271,10 @@
|
||||
"devOptional": true,
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
|
||||
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -1078,6 +1058,37 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.0.0-rc.17",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
||||
@@ -1154,6 +1165,7 @@
|
||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -1200,6 +1212,7 @@
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1290,6 +1303,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
@@ -1447,6 +1461,7 @@
|
||||
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
@@ -2487,6 +2502,7 @@
|
||||
"integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.5.0",
|
||||
"colorjs.io": "^0.5.0",
|
||||
@@ -2981,6 +2997,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
||||
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
@@ -3114,6 +3131,7 @@
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user