feat : ajout de Malio UI et de la conf gitea
This commit is contained in:
23
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
23
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
name: "Merge Request"
|
||||||
|
about: "Template de MR"
|
||||||
|
title: "[#NUMERO_TICKET] TITRE TICKET"
|
||||||
|
ref: "main"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
| Numéro du ticket | Titre du ticket |
|
||||||
|
|------------------|-----------------|
|
||||||
|
| | |
|
||||||
|
|
||||||
|
## Description de la PR
|
||||||
|
|
||||||
|
## Modification du .env
|
||||||
|
|
||||||
|
## Check list
|
||||||
|
|
||||||
|
- [ ] Pas de régression
|
||||||
|
- [ ] TU/TI/TF rédigée
|
||||||
|
- [ ] TU/TI/TF OK
|
||||||
|
- [ ] CHANGELOG modifié
|
||||||
65
.gitea/workflows/auto-tag-develop.yml
Normal file
65
.gitea/workflows/auto-tag-develop.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Auto Tag Develop
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
persist-credentials: true
|
||||||
|
|
||||||
|
- name: Create next tag from config/version.yaml
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Skip if current commit already has a vX.Y.Z tag
|
||||||
|
if git tag --points-at HEAD | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||||
|
echo "Tag already exists on this commit. Skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
changed_version=false
|
||||||
|
if git diff --name-only "${{ gitea.event.before }}" "${{ gitea.event.after }}" | grep -q '^config/version\.yaml$'; then
|
||||||
|
changed_version=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
read_version() {
|
||||||
|
awk -F': *' '/app\.version:/{print $2}' config/version.yaml | tr -d '[:space:]' | tr -d "'\""
|
||||||
|
}
|
||||||
|
|
||||||
|
if $changed_version; then
|
||||||
|
version="$(read_version)"
|
||||||
|
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Invalid version in version.yaml: $version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
last_tag="$(git tag -l 'v*' --sort=-v:refname | head -n1 || true)"
|
||||||
|
if [ -z "$last_tag" ]; then
|
||||||
|
version="0.1.0"
|
||||||
|
else
|
||||||
|
base="${last_tag#v}"
|
||||||
|
IFS='.' read -r major minor patch <<< "$base"
|
||||||
|
version="${major}.${minor}.$((patch + 1))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "parameters:\\n app.version: '%s'\\n" "$version" > config/version.yaml
|
||||||
|
git config user.name "gitea-actions"
|
||||||
|
git config user.email "gitea-actions@local"
|
||||||
|
git add config/version.yaml
|
||||||
|
git commit -m "chore: bump version to v$version" || true
|
||||||
|
git push origin develop || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="v$version"
|
||||||
|
git tag "$tag"
|
||||||
|
git push origin "$tag"
|
||||||
65
.gitea/workflows/release-artefact.yml
Normal file
65
.gitea/workflows/release-artefact.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Build Release Artefact
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.4"
|
||||||
|
extensions: mbstring, intl, pdo_pgsql, xml, curl, zip, gd
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "lts/*"
|
||||||
|
|
||||||
|
- name: Install backend deps (prod)
|
||||||
|
env:
|
||||||
|
APP_ENV: prod
|
||||||
|
APP_DEBUG: "0"
|
||||||
|
run: composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
|
||||||
|
|
||||||
|
- name: Build frontend (static)
|
||||||
|
run: |
|
||||||
|
cd frontend
|
||||||
|
npm ci
|
||||||
|
CI=1 NUXT_TELEMETRY_DISABLED=1 NUXT_PUBLIC_API_BASE=/api NUXT_PUBLIC_APP_BASE=/ npm run generate
|
||||||
|
test -f .output/public/index.html
|
||||||
|
|
||||||
|
- name: Build artefact
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p release
|
||||||
|
tar -czf "release/lesstime-${GITHUB_REF_NAME}.tar.gz" \
|
||||||
|
bin \
|
||||||
|
config \
|
||||||
|
migrations \
|
||||||
|
public \
|
||||||
|
src \
|
||||||
|
templates \
|
||||||
|
vendor \
|
||||||
|
composer.json \
|
||||||
|
composer.lock \
|
||||||
|
symfony.lock \
|
||||||
|
frontend/.output
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: release/lesstime-${{ github.ref_name }}.tar.gz
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
93
CLAUDE.md
Normal file
93
CLAUDE.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Lesstime
|
||||||
|
|
||||||
|
Application de gestion de projet. Monorepo Symfony 8 (API Platform 4) + Nuxt 4.
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **Backend** : PHP 8.4, Symfony 8.0, API Platform 4, Doctrine ORM, PostgreSQL 16
|
||||||
|
- **Frontend** : Nuxt 4 (SSR off / SPA), Vue 3, Pinia, Tailwind CSS, nuxt-toast, @nuxtjs/i18n, @nuxt/icon
|
||||||
|
- **Auth** : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login à `/login_check`, cookie `BEARER`
|
||||||
|
- **Docker** : PHP-FPM + Node 24, Nginx (port 8082), PostgreSQL (port 5435)
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/Entity/ # Entités Doctrine
|
||||||
|
src/ApiResource/ # Ressources API Platform (si découplées des entités)
|
||||||
|
src/State/ # Providers et Processors API Platform
|
||||||
|
src/Repository/ # Repositories Doctrine
|
||||||
|
src/DataFixtures/ # Fixtures
|
||||||
|
config/ # Config Symfony (security, api_platform, lexik_jwt, nelmio_cors, doctrine)
|
||||||
|
config/jwt/ # Clés JWT (private.pem, public.pem)
|
||||||
|
migrations/ # Migrations Doctrine
|
||||||
|
frontend/ # App Nuxt 4
|
||||||
|
frontend/pages/ # Pages
|
||||||
|
frontend/layouts/ # Layouts (pas "layout")
|
||||||
|
frontend/components/ # Composants Vue
|
||||||
|
frontend/composables/# Composables (useApi, etc.)
|
||||||
|
frontend/stores/ # Stores Pinia
|
||||||
|
frontend/services/ # Services API (auth, etc.)
|
||||||
|
frontend/services/dto/ # Types TypeScript
|
||||||
|
frontend/i18n/locales/ # Fichiers de traduction (langDir résolu depuis i18n/)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commandes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make start # Démarrer les containers
|
||||||
|
make install # Install complet (composer, migrations, fixtures, build Nuxt)
|
||||||
|
make reset # Tout supprimer et réinstaller (supprime la BDD)
|
||||||
|
make dev-nuxt # Dev server Nuxt (hot reload, port 3002)
|
||||||
|
make shell # Shell dans le container PHP
|
||||||
|
make migration-migrate # Lancer les migrations
|
||||||
|
make fixtures # Charger les fixtures
|
||||||
|
make db-reset # Reset BDD + migrations + fixtures
|
||||||
|
make test # PHPUnit
|
||||||
|
make php-cs-fixer-allow-risky # Fix code style PHP
|
||||||
|
make logs-dev # Tail logs Symfony
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
Format : `<type>(<scope optionnel>) : <message>` (espace avant et après `:`)
|
||||||
|
|
||||||
|
Types autorisés (minuscules) : `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`
|
||||||
|
|
||||||
|
Exemples : `feat : add login page`, `fix(auth) : prevent null token crash`
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
- Toujours `declare(strict_types=1)` en haut des fichiers PHP
|
||||||
|
- API Platform : utiliser ApiResource, Providers (`src/State/`), Processors — pas de controllers
|
||||||
|
- Routes API préfixées `/api` (via `config/routes/api_platform.yaml`)
|
||||||
|
- Le login (`/login_check`) est hors prefix `/api`, nginx réécrit `REQUEST_URI` vers `/login_check`
|
||||||
|
- PHP CS Fixer : règles Symfony + PSR-12 + strict types
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
- TypeScript strict
|
||||||
|
- Composable `useApi()` pour tous les appels API (gère cookies, erreurs, toasts, i18n)
|
||||||
|
- Store Pinia pour l'auth (`useAuthStore`)
|
||||||
|
- Middleware global `auth.global.ts` protège les routes
|
||||||
|
- Traductions dans `frontend/i18n/locales/` (le module résout `langDir` depuis `i18n/`)
|
||||||
|
- 4 espaces d'indentation
|
||||||
|
|
||||||
|
### Nginx
|
||||||
|
|
||||||
|
- `/api/*` → Symfony (via try_files + index.php)
|
||||||
|
- `/api/login_check` → location exact match, fastcgi direct avec REQUEST_URI réécrit en `/login_check`
|
||||||
|
- `/` → SPA frontend (`frontend/dist/`)
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
- Container PHP : `php-lesstime-fpm`
|
||||||
|
- Container Nginx : `nginx-lesstime`
|
||||||
|
- Container DB : PostgreSQL sur port **5435** (interne et externe)
|
||||||
|
- Config Docker : `docker/.env.docker` (override local : `docker/.env.docker.local`)
|
||||||
|
- Après modif nginx : `docker restart nginx-lesstime`
|
||||||
|
|
||||||
|
## Fixtures
|
||||||
|
|
||||||
|
- User admin : `admin` / `admin` (ROLE_ADMIN)
|
||||||
1
frontend/.npmrc
Normal file
1
frontend/.npmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@malio:registry=https://gitea.malio.fr/api/packages/MALIO-DEV/npm/
|
||||||
@@ -7,12 +7,13 @@ export default defineNuxtConfig({
|
|||||||
? (process.env.NUXT_PUBLIC_APP_BASE || '/')
|
? (process.env.NUXT_PUBLIC_APP_BASE || '/')
|
||||||
: '/'
|
: '/'
|
||||||
},
|
},
|
||||||
|
extends: ['@malio/layer-ui'],
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxtjs/tailwindcss',
|
'@nuxtjs/tailwindcss',
|
||||||
'@pinia/nuxt',
|
'@pinia/nuxt',
|
||||||
'nuxt-toast',
|
'nuxt-toast',
|
||||||
'@nuxtjs/i18n',
|
'@nuxtjs/i18n',
|
||||||
'@nuxt/icon'
|
'@nuxt/icon',
|
||||||
],
|
],
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
|
|||||||
31
frontend/package-lock.json
generated
31
frontend/package-lock.json
generated
@@ -7,6 +7,7 @@
|
|||||||
"name": "nuxt-app",
|
"name": "nuxt-app",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@malio/layer-ui": "^1.1.0",
|
||||||
"@nuxt/icon": "^2.2.1",
|
"@nuxt/icon": "^2.2.1",
|
||||||
"@nuxtjs/i18n": "^10.2.3",
|
"@nuxtjs/i18n": "^10.2.3",
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
@@ -2123,6 +2124,20 @@
|
|||||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@malio/layer-ui": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://gitea.malio.fr/api/packages/MALIO-DEV/npm/%40malio%2Flayer-ui/-/1.1.0/layer-ui-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-mc+kOK+EDfo6ZZcE0/FaVnvDyIDJrigkgOzvL8rxnpljXEiRlKj5673e5e6ZIoOyKFqktzbJXzFr4V6UBD0wPg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/icon": "^2.2.1",
|
||||||
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
"maska": "^3.2.0",
|
||||||
|
"tailwind-merge": "^3.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"nuxt": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mapbox/node-pre-gyp": {
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz",
|
||||||
@@ -9483,6 +9498,12 @@
|
|||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/maska": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/maska/-/maska-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-zSmSgs5/q9vMSmrdZT3rKOv9uLznNWR/niuuAdBZDTvB3SMKOX9vhMtDijFyExz+B4UClu2rvksylUh/ea1bLA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -12424,6 +12445,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.19",
|
"version": "3.4.19",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"build:dist": "nuxt generate && rm -rf dist && cp -R .output/public dist"
|
"build:dist": "nuxt generate && rm -rf dist && cp -R .output/public dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@malio/layer-ui": "^1.1.0",
|
||||||
"@nuxt/icon": "^2.2.1",
|
"@nuxt/icon": "^2.2.1",
|
||||||
"@nuxtjs/i18n": "^10.2.3",
|
"@nuxtjs/i18n": "^10.2.3",
|
||||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
|||||||
@@ -9,18 +9,13 @@
|
|||||||
class="mt-8 space-y-6 rounded-lg border border-neutral-200 bg-white p-6 shadow-sm"
|
class="mt-8 space-y-6 rounded-lg border border-neutral-200 bg-white p-6 shadow-sm"
|
||||||
@submit.prevent="handleSubmit"
|
@submit.prevent="handleSubmit"
|
||||||
>
|
>
|
||||||
<div>
|
<MalioInputText
|
||||||
<label class="text-sm font-semibold text-neutral-700" for="username">
|
label="Nom d'utilisateur"
|
||||||
Nom d'utilisateur
|
autocomplete="username"
|
||||||
</label>
|
group-class="mt-0"
|
||||||
<input
|
inputClass="w-full"
|
||||||
id="username"
|
v-model="username"
|
||||||
v-model="username"
|
/>
|
||||||
type="text"
|
|
||||||
autocomplete="username"
|
|
||||||
class="mt-2 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="text-sm font-semibold text-neutral-700" for="password">
|
<label class="text-sm font-semibold text-neutral-700" for="password">
|
||||||
@@ -55,7 +50,7 @@ useHead({
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const { version } = useAppVersion()
|
const {version} = useAppVersion()
|
||||||
|
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
|
|||||||
Reference in New Issue
Block a user