Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fb80ef59f | ||
|
|
53ceb29bbe | ||
|
|
7862bb11b7 | ||
|
|
42b3d28505 | ||
|
|
658be24b7c | ||
|
|
a2fbbe5d44 | ||
|
|
e1447aca28 | ||
|
|
4755b392a3 | ||
|
|
445a540d54 | ||
|
|
1dd4b4d1d1 | ||
|
|
7bb7484ec8 | ||
|
|
940834d993 | ||
|
|
fa0aa808ac | ||
|
|
002764ea9a | ||
|
|
b4d7f7e10f | ||
|
|
029fbe5d26 | ||
|
|
fe16d01be7 | ||
|
|
7c4204c689 | ||
|
|
27685ee250 | ||
|
|
236b8fe037 | ||
|
|
e9fd40f27d | ||
|
|
6c304d461c | ||
|
|
acb475d5d0 | ||
|
|
9d8ae3d32a | ||
|
|
cc8df1a4af | ||
|
|
b69850a23b | ||
|
|
b8ec3f2828 | ||
|
|
cdd6e1c573 | ||
|
|
89c24b9cd8 | ||
|
|
5c47f1f7c6 | ||
|
|
bc2cfdb1c3 | ||
|
|
5d7d158436 | ||
|
|
240988ed61 | ||
|
|
3553f1d839 | ||
|
|
d326f24f8d | ||
|
|
a5a71bcd8b | ||
|
|
16a346e8fe | ||
|
|
59acaf46cb | ||
|
|
c69b917f94 | ||
|
|
484f0313b2 | ||
|
|
c38ea72928 | ||
|
|
95ab0b68ae | ||
|
|
e29b7401d6 | ||
|
|
45fc4c56d9 | ||
|
|
be86854c7e | ||
|
|
7b236f6770 | ||
|
|
f149571673 | ||
|
|
2dd08c0d46 | ||
|
|
982d45994d | ||
|
|
c008d38d10 | ||
|
|
c76f5e958c | ||
|
|
f6f138ea97 | ||
|
|
9264854a54 | ||
|
|
ae6f88a02e | ||
|
|
a9f02bd47d | ||
|
|
eec2e0c52f | ||
|
|
aa04c62904 | ||
|
|
42b829fffc | ||
|
|
6ecf94dc9f | ||
|
|
b7911fcdae | ||
|
|
d8e9503cc7 | ||
|
|
166248078f | ||
|
|
f431a0b7cc | ||
|
|
4b111ef02d | ||
|
|
2d72898bdc | ||
|
|
78021a5496 | ||
|
|
a4bc477520 | ||
|
|
7097490439 | ||
|
|
53c9e1253d | ||
|
|
aa0a7a4efd | ||
|
|
1492e49b5e | ||
|
|
a5ace8b0fa | ||
|
|
13a8730692 | ||
|
|
2ab2e1f261 | ||
|
|
52ff55a291 | ||
|
|
0147a86fac | ||
|
|
e9f16769a9 | ||
|
|
a4f279480b | ||
|
|
112ba401d7 | ||
|
|
3d12bbdc8d | ||
|
|
bd49ae940d | ||
|
|
282ffbf9d8 | ||
|
|
44b777b69d | ||
|
|
c4aada1caa | ||
|
|
a1a5849dd4 | ||
|
|
e357330cad | ||
|
|
062b4e2162 | ||
|
|
4f8d495579 | ||
|
|
cdbacdd434 | ||
|
|
c8263df37d | ||
|
|
82fada4f32 | ||
|
|
376e82570f | ||
|
|
66fe78f86e | ||
|
|
005ed9ee7f | ||
|
|
9a055add6f | ||
|
|
99cb96bbd6 | ||
|
|
9894a8b2fb | ||
|
|
57c9d26cb3 | ||
|
|
4b9073b8ee | ||
|
|
4ef4c77c3a | ||
|
|
82cad3b56c | ||
|
|
9c860e25cc | ||
|
|
cd013ae162 | ||
|
|
7a301456fe | ||
|
|
5a70b6214f | ||
|
|
52668323e1 | ||
|
|
00a000dc48 | ||
|
|
97577b32c5 | ||
|
|
322fc20875 | ||
|
|
7eaa3e3c09 | ||
|
|
db00e2d814 | ||
|
|
01d506ec76 | ||
|
|
647b5dbd3b | ||
|
|
56f227e939 | ||
|
|
387b3f4806 | ||
|
|
e1842c5df5 | ||
|
|
56f010c21c | ||
|
|
13b0486104 | ||
|
|
e218ab9a0e | ||
|
|
ece48d7a4e | ||
|
|
991e557b68 | ||
|
|
dc8cd06989 | ||
|
|
3ede12865b | ||
|
|
56401bae09 | ||
|
|
73331e27c1 | ||
|
|
8e932be47f | ||
|
|
0d4f6f3929 | ||
|
|
66187011b6 | ||
|
|
f15eccdf5c | ||
|
|
74214fc286 | ||
|
|
89a5446889 | ||
|
|
74519f7e41 | ||
|
|
df4948c19a | ||
|
|
7bc6c695b8 | ||
|
|
946a0f93b3 | ||
|
|
98992b0e1d | ||
|
|
02a23a09ee | ||
|
|
a967840079 | ||
|
|
ba45a934cf | ||
|
|
47fb1d9727 | ||
|
|
7986bc70b1 | ||
|
|
19e85089ab | ||
|
|
91b820d289 | ||
|
|
6c4904875b | ||
|
|
8d32db8674 | ||
|
|
328c0dc2f5 | ||
|
|
475d6958dc | ||
|
|
9b546f6d77 | ||
|
|
3b1203e3f7 | ||
|
|
ba1c7ffb4d | ||
|
|
e125fcb1ab | ||
|
|
efa3c1c1cd | ||
|
|
5b03ef4c10 | ||
|
|
083c82eb1e | ||
|
|
e830bdb69c | ||
|
|
327c23c33f | ||
|
|
58eb66fc7f | ||
|
|
ac14204dc6 | ||
|
|
1fcad7fd95 | ||
|
|
801af0df91 | ||
|
|
6b626f861d | ||
|
|
c83946223b | ||
|
|
764bd31f1c | ||
|
|
29c171d4b1 | ||
|
|
0559094aba | ||
|
|
805c3d4524 | ||
|
|
0645e6b7b3 | ||
|
|
789ee885a2 | ||
|
|
821bce7476 | ||
|
|
5fafcfd604 | ||
|
|
d6166c788a | ||
|
|
987ffb2da1 | ||
|
|
c84392a7ff | ||
|
|
ecfd69504c | ||
|
|
f3108d4d84 | ||
|
|
e7c79bf033 | ||
|
|
8c208554d5 | ||
|
|
c5cac7f7f8 | ||
|
|
c2f4867a55 | ||
|
|
9d38590fc1 | ||
|
|
3c3e984655 | ||
|
|
89865cbba5 | ||
|
|
cf7a0618bc | ||
|
|
de4e0a32db | ||
|
|
6314bb7e51 | ||
|
|
055e4a9281 | ||
|
|
6add0ece80 | ||
|
|
befb91672d |
@@ -3,5 +3,6 @@
|
||||
/.git*
|
||||
/.nuxt
|
||||
/.output
|
||||
/docker
|
||||
/node_modules
|
||||
/.techradar
|
||||
/build
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -13,6 +13,13 @@ src/rd.json
|
||||
radar.backup
|
||||
config.json.backup
|
||||
|
||||
# radar-app build artifacts (le code source doit être versionné)
|
||||
radar-app/node_modules
|
||||
radar-app/out
|
||||
radar-app/build
|
||||
radar-app/.next
|
||||
radar-app/.turbo
|
||||
|
||||
# Fichiers temporaires générés par serve-business.sh
|
||||
radar/*.md
|
||||
!radar-business/**/*.md
|
||||
@@ -20,3 +27,8 @@ public/inline-strategie.js
|
||||
public/strategie-content-raw.txt
|
||||
public/strategie-content.js
|
||||
public/strategie-inline.html
|
||||
|
||||
# Docs
|
||||
docs/
|
||||
docs-sbom/
|
||||
docs-syoul/
|
||||
|
||||
158
.woodpecker.yml
Normal file
158
.woodpecker.yml
Normal file
@@ -0,0 +1,158 @@
|
||||
when:
|
||||
- branch:
|
||||
- main
|
||||
- stand-alone
|
||||
event: push
|
||||
|
||||
steps:
|
||||
|
||||
# Etape 0 : Validation syntaxique du docker-compose
|
||||
# Les vars CI (CI_REPO_OWNER, CI_COMMIT_BRANCH) sont injectees automatiquement par Woodpecker
|
||||
- name: validate
|
||||
image: docker:27-cli
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
RADAR_DOMAIN: validate.example.com
|
||||
commands:
|
||||
- |
|
||||
export COMPOSE_PROJECT_NAME=$(printf '%s-%s-%s' "$CI_REPO_OWNER" "$CI_REPO_NAME" "$CI_COMMIT_BRANCH" | tr 'A-Z/' 'a-z-')
|
||||
docker compose -f docker-compose.business.yml config --quiet
|
||||
- echo "docker-compose.business.yml valide"
|
||||
|
||||
# Etape 1 : Build de l'application statique
|
||||
- name: build
|
||||
image: node:20-alpine
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
HUSKY: 0
|
||||
HUSKY_SKIP_INSTALL: 1
|
||||
commands:
|
||||
- apk add --no-cache git python3
|
||||
- npm ci --legacy-peer-deps
|
||||
- cd radar-app && npm ci --legacy-peer-deps --include=dev && npm run build:icons && cd ..
|
||||
- node scripts/generate-team-visualization-data.js
|
||||
- npm run build
|
||||
- ls -la build/ | head -10
|
||||
|
||||
# Etape 2a : Ecriture du .env depuis les secrets
|
||||
# NOTE: from_secret et volumes: incompatibles dans le meme step (bug Woodpecker next)
|
||||
- name: write-env
|
||||
image: alpine:3.20
|
||||
environment:
|
||||
RADAR_DOMAIN:
|
||||
from_secret: radar_domain
|
||||
commands:
|
||||
- env | grep -E "^(RADAR_DOMAIN)=" > .env.deploy
|
||||
# COMPOSE_PROJECT_NAME : convention user-project-branch, genere depuis les vars CI
|
||||
- OWNER=$(echo "$CI_REPO_OWNER" | tr 'A-Z' 'a-z') && REPO=$(echo "$CI_REPO_NAME" | tr 'A-Z' 'a-z') && BRANCH=$(echo "$CI_COMMIT_BRANCH" | tr 'A-Z/' 'a-z-') && echo "COMPOSE_PROJECT_NAME=$OWNER-$REPO-$BRANCH" >> .env.deploy
|
||||
- echo "Fichier .env.deploy cree ($(wc -c < .env.deploy) octets)"
|
||||
|
||||
# Etape 2b : Deploiement sur sonic via Docker socket
|
||||
# NOTE: from_secret et volumes: incompatibles — pas de secrets ici
|
||||
- name: deploy
|
||||
image: docker:27-cli
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- docker compose --env-file .env.deploy -f docker-compose.business.yml build --no-cache
|
||||
- docker compose --env-file .env.deploy -f docker-compose.business.yml up -d --remove-orphans
|
||||
- docker compose --env-file .env.deploy -f docker-compose.business.yml ps
|
||||
- |
|
||||
DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2)
|
||||
|
||||
# --- Certificat TLS (acme.sh via sonic-acme-1) ---
|
||||
# acme.sh est idempotent : skip si cert valide, renouvelle si proche expiration
|
||||
# exit 0 = emis/renouvele, exit 2 = skip (domaine inchange), autres = erreur
|
||||
ACME_EXIT=0
|
||||
docker exec sonic-acme-1 /app/acme.sh \
|
||||
--home /etc/acme.sh \
|
||||
--issue -d "$DOMAIN" \
|
||||
--webroot /usr/share/nginx/html \
|
||||
--server letsencrypt \
|
||||
--accountemail support+acme@asycn.io || ACME_EXIT=$?
|
||||
if [ "$ACME_EXIT" -ne 0 ] && [ "$ACME_EXIT" -ne 2 ]; then
|
||||
echo "ERREUR: acme.sh a echoue (exit $ACME_EXIT)"
|
||||
exit 1
|
||||
fi
|
||||
docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/fullchain.cer /host/certs/$DOMAIN-cert.pem
|
||||
docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/$DOMAIN.key /host/certs/$DOMAIN-key.pem
|
||||
echo "Cert TLS: /host/certs/$DOMAIN-cert.pem OK (acme exit $ACME_EXIT)"
|
||||
|
||||
# Etape 3a : Generation SBOM (Syft) — inventaire des dependances npm du workspace
|
||||
# Scan du repertoire source (pas de docker socket = pas de bug volumes/CI-vars)
|
||||
- name: sbom-generate
|
||||
image: alpine:3.20
|
||||
commands:
|
||||
- apk add --no-cache curl
|
||||
- curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin latest
|
||||
- mkdir -p .reports
|
||||
- syft dir:. --exclude './build' --exclude './radar-app/.next' --exclude './radar-app/out' -o cyclonedx-json --file .reports/sbom-radar.cyclonedx.json
|
||||
- echo "SBOM genere $(wc -c < .reports/sbom-radar.cyclonedx.json) octets"
|
||||
|
||||
# Etape 3b : Scan CVE (Trivy) depuis le SBOM Syft
|
||||
# Cache /home/syoul/trivy-cache evite ~200Mo de telechargement des DB CVE a chaque build
|
||||
# Prerequis sur sonic : mkdir -p /home/syoul/trivy-cache
|
||||
- name: sbom-scan
|
||||
image: aquasec/trivy:latest
|
||||
volumes:
|
||||
- /home/syoul/trivy-cache:/root/.cache/trivy
|
||||
commands:
|
||||
- trivy sbom --format json --output .reports/trivy-radar.json .reports/sbom-radar.cyclonedx.json
|
||||
- echo "Scan CVE termine"
|
||||
|
||||
# Etape 3c : Publication SBOM vers Dependency-Track (dtrack.syoul.fr)
|
||||
# NOTE: from_secret et volumes: incompatibles — pas de volumes ici
|
||||
- name: sbom-publish
|
||||
image: alpine/curl:latest
|
||||
environment:
|
||||
DTRACK_TOKEN:
|
||||
from_secret: dependency_track_token
|
||||
commands:
|
||||
- |
|
||||
VERSION=$(date +%Y-%m-%d)-$(echo "$CI_COMMIT_SHA" | cut -c1-8)
|
||||
HTTP=$(curl -s -o /tmp/dtrack-response.txt -w "%{http_code}" -X POST "https://dtrack.syoul.fr/api/v1/bom" \
|
||||
-H "X-Api-Key: $DTRACK_TOKEN" \
|
||||
-F "autoCreate=true" \
|
||||
-F "projectName=techradardev-app" \
|
||||
-F "projectVersion=$VERSION" \
|
||||
-F "bom=@.reports/sbom-radar.cyclonedx.json")
|
||||
echo "HTTP $HTTP : $(cat /tmp/dtrack-response.txt)"
|
||||
[ "$HTTP" -ge 200 ] && [ "$HTTP" -lt 300 ] || exit 1
|
||||
|
||||
# Etape 4 : Healthcheck post-deploiement
|
||||
- name: healthcheck
|
||||
image: alpine:3.20
|
||||
commands:
|
||||
- apk add --no-cache --quiet curl
|
||||
- |
|
||||
DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2)
|
||||
if [ -z "$DOMAIN" ]; then
|
||||
echo "ERREUR: RADAR_DOMAIN non defini dans .env.deploy"
|
||||
exit 1
|
||||
fi
|
||||
TARGET="https://$DOMAIN"
|
||||
echo "Healthcheck sur $TARGET (max 100s)..."
|
||||
MAX=20
|
||||
i=0
|
||||
until [ $i -ge $MAX ]; do
|
||||
CODE=$(curl -sSo /dev/null -w "%{http_code}" "$TARGET" 2>/dev/null)
|
||||
echo "Tentative $((i+1))/$MAX - HTTP $CODE"
|
||||
if [ "$CODE" = "200" ] || [ "$CODE" = "301" ] || [ "$CODE" = "302" ]; then
|
||||
echo "Radar repond sur $TARGET"
|
||||
exit 0
|
||||
fi
|
||||
i=$((i+1))
|
||||
sleep 5
|
||||
done
|
||||
echo "ERREUR: Radar ne repond pas apres 100 secondes"
|
||||
exit 1
|
||||
|
||||
# Notification en cas d'echec
|
||||
- name: notify-failure
|
||||
image: alpine:3.20
|
||||
commands:
|
||||
- 'echo "ECHEC pipeline #$CI_BUILD_NUMBER sur commit $CI_COMMIT_SHA"'
|
||||
- 'echo "Branche: $CI_COMMIT_BRANCH"'
|
||||
when:
|
||||
- status: failure
|
||||
@@ -1,40 +1,190 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# Utiliser une image Node.js légère
|
||||
FROM node:20-alpine
|
||||
|
||||
# Build arguments pour invalider le cache si nécessaire
|
||||
ARG BUILD_DATE=unknown
|
||||
ARG BUILD_VERSION=unknown
|
||||
ARG CACHE_BUST=1
|
||||
LABEL build.date="${BUILD_DATE}" \
|
||||
build.version="${BUILD_VERSION}" \
|
||||
cache.bust="${CACHE_BUST}"
|
||||
|
||||
# Invalider le cache en utilisant CACHE_BUST dans une instruction RUN
|
||||
# Cela force Docker à reconstruire à partir de cette ligne si CACHE_BUST change
|
||||
# Utiliser CACHE_BUST dans une variable d'environnement pour forcer l'invalidation
|
||||
RUN echo "Cache bust: ${CACHE_BUST}" && \
|
||||
echo "Build date: ${BUILD_DATE}" && \
|
||||
date && \
|
||||
echo "${CACHE_BUST}" > /tmp/cache_bust.txt
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Variables d'environnement à définir AVANT npm install
|
||||
ENV HUSKY=0
|
||||
ENV HUSKY_SKIP_INSTALL=1
|
||||
ENV NODE_PATH=/app/node_modules
|
||||
ENV NODE_ENV=development
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Installation des dépendances système
|
||||
RUN apk add --no-cache git
|
||||
RUN apk add --no-cache git python3
|
||||
|
||||
# Copie des fichiers de dépendances
|
||||
# Copie des fichiers de dépendances racine
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Installation des dépendances Node
|
||||
# Installation des dépendances Node racine (pour scripts: generate-team-visualization-data, etc.)
|
||||
RUN npm install --legacy-peer-deps --ignore-scripts
|
||||
|
||||
# Patch du package aoe_technology_radar pour inclure gray-matter dans les dépendances runtime
|
||||
RUN node -e "const fs=require('fs');const pkgPath='./node_modules/aoe_technology_radar/package.json';const pkg=JSON.parse(fs.readFileSync(pkgPath,'utf8'));pkg.dependencies=pkg.dependencies||{};pkg.dependencies['gray-matter']='^4.0.3';pkg.dependencies['postcss']='^8.4.47';pkg.scripts=pkg.scripts||{};pkg.scripts.prepare='';fs.writeFileSync(pkgPath,JSON.stringify(pkg,null,2));"
|
||||
|
||||
# Copie du reste du projet
|
||||
# Copie du reste du projet (inclut radar-app/)
|
||||
COPY . .
|
||||
RUN chmod +x scripts/start-business.sh
|
||||
|
||||
# Préparer .techradar une fois pour toutes (évite les réinstallations au runtime)
|
||||
RUN npx techradar install && \
|
||||
node -e "const fs=require('fs');const p='.techradar/package.json';const pkg=JSON.parse(fs.readFileSync(p,'utf8'));pkg.scripts=pkg.scripts||{};pkg.scripts.prepare='';fs.writeFileSync(p,JSON.stringify(pkg,null,2));" && \
|
||||
cd .techradar && npm install --legacy-peer-deps && npm run build:icons && cd ..
|
||||
# Installer les dépendances dans radar-app (Next.js et dépendances du framework)
|
||||
# Désactiver le script prepare (husky) pour éviter les erreurs
|
||||
RUN cd radar-app && \
|
||||
node -e "const fs=require('fs');const p='package.json';const pkg=JSON.parse(fs.readFileSync(p,'utf8'));pkg.scripts=pkg.scripts||{};pkg.scripts.prepare='';fs.writeFileSync(p,JSON.stringify(pkg,null,2));" && \
|
||||
npm install --legacy-peer-deps --include=dev cytoscape cytoscape-cose-bilkent echarts-for-react postcss && \
|
||||
npm run build:icons
|
||||
|
||||
# --- CONFIGURATION BUSINESS ---
|
||||
# Application de la logique Business (remplacement de la config et des données)
|
||||
# Préserver la structure de dossiers par date pour que le framework puisse parser les dates
|
||||
RUN cp radar-business/config-business.json config.json && \
|
||||
rm -rf radar/* && \
|
||||
cp -r radar-business/2025-01-15/* radar/
|
||||
mkdir -p radar/2025-01-15 && \
|
||||
cp -r radar-business/2025-01-15/* radar/2025-01-15/
|
||||
|
||||
# Toujours régénérer les données de visualisation équipe (pour avoir les bons rings)
|
||||
RUN echo "🔄 Régénération des données de visualisation équipe..." && \
|
||||
node scripts/generate-team-visualization-data.js && \
|
||||
echo "✅ Données de visualisation équipe générées" && \
|
||||
echo "🔍 Vérification contenu team-visualization-data.json:" && \
|
||||
ls -la public/team-visualization-data.json && \
|
||||
head -20 public/team-visualization-data.json
|
||||
|
||||
# Copier les fichiers nécessaires dans radar-app avant le build
|
||||
RUN rm -rf radar-app/data/radar && \
|
||||
mkdir -p radar-app/data/radar/2025-01-15 && \
|
||||
cp -r radar-business/2025-01-15/* radar-app/data/radar/2025-01-15/ && \
|
||||
# Supprimer toute release de démo (2017-03-01, 2024-03-01, etc.) éventuellement présentes
|
||||
find radar-app/data/radar -mindepth 1 -maxdepth 1 ! -name '2025-01-15' -exec rm -rf {} + && \
|
||||
cp radar-business/config-business.json radar-app/data/config.json && \
|
||||
rm -rf radar-app/public && mkdir -p radar-app/public && \
|
||||
cp -r public/* radar-app/public/ && \
|
||||
cp public/_team-content radar-app/public/_team-content 2>/dev/null || true && \
|
||||
cp public/team-visualization-data.json radar-app/public/team-visualization-data.json 2>/dev/null || true && \
|
||||
cp about.md radar-app/data/about.md 2>/dev/null || echo "about.md not found, skipping" && \
|
||||
cp custom.css radar-app/src/styles/custom.css 2>/dev/null || echo "custom.css not found, skipping" && \
|
||||
echo "Fichiers public copiés" && \
|
||||
echo "📁 Vérification des fichiers team dans radar-app/public/:" && \
|
||||
ls -la radar-app/public/ | grep -E "(team\.html|team-visualization)" && echo "✅ Fichiers team trouvés" || (echo "⚠️ Fichiers team non trouvés dans radar-app/public/" && echo "📁 Contenu de public/ source:" && ls -la public/ | head -10) && \
|
||||
echo "📁 Vérification que _team-content existe dans public/ source:" && \
|
||||
test -f public/_team-content && echo "✅ public/_team-content existe" || echo "❌ public/_team-content n'existe pas"
|
||||
|
||||
# Diagnostic : compter les fichiers markdown copiés dans radar-app/data/radar
|
||||
RUN echo "📊 Comptage des fichiers .md dans radar-app/data/radar" && \
|
||||
find radar-app/data/radar -name "*.md" | wc -l && \
|
||||
find radar-app/data/radar -name "*.md" | head -10
|
||||
|
||||
# Créer la page Next.js /team ET un fichier HTML statique /team/index.html
|
||||
# La page Next.js pour le routing, le HTML statique pour garantir l'affichage
|
||||
RUN mkdir -p radar-app/src/pages
|
||||
COPY docker/team-page.tsx radar-app/src/pages/team.tsx
|
||||
|
||||
# Modifier _document.tsx pour charger team-block-script.js en premier (avant le rendu)
|
||||
COPY docker/patch_document.py /tmp/patch_document.py
|
||||
RUN python3 /tmp/patch_document.py && \
|
||||
echo "📄 _document.tsx apres modification:" && \
|
||||
cat radar-app/src/pages/_document.tsx
|
||||
|
||||
# Script Python pour ajouter le lien Équipe dans Navigation.tsx (supprime TOUS les doublons)
|
||||
COPY docker/add_team_link.py /tmp/add_team_link.py
|
||||
|
||||
# Script shell pour gérer l'ajout du lien Équipe
|
||||
COPY docker/add_team_link.sh /tmp/add_team_link.sh
|
||||
RUN chmod +x /tmp/add_team_link.sh && \
|
||||
echo "🔍 VÉRIFICATION: Scripts modifiés:" && \
|
||||
echo "=== team-block-script.js ===" && \
|
||||
head -10 public/team-block-script.js && \
|
||||
echo "=== strategie-script.js ===" && \
|
||||
grep -A 2 -B 1 "__blockTeamPages" public/strategie-script.js || echo "❌ Protection non trouvée" && \
|
||||
echo "=== config-business.json ===" && \
|
||||
grep "jsFile" radar-business/config-business.json
|
||||
|
||||
# Exécuter le script
|
||||
RUN /tmp/add_team_link.sh
|
||||
|
||||
# Builder l'application en mode production pour éviter Fast Refresh
|
||||
# Utiliser WORKDIR pour changer de répertoire de manière fiable
|
||||
WORKDIR /app/radar-app
|
||||
RUN npm run build:data
|
||||
RUN npm run build
|
||||
# S'assurer que _team-content.html et team-visualization-data.json sont copiés dans out/
|
||||
# Next.js en mode export copie automatiquement les fichiers de public/, mais vérifions quand même
|
||||
RUN if [ -d "out" ]; then \
|
||||
echo "📁 Contenu de out/ avant copie:" && \
|
||||
ls -la out/ | head -10 && \
|
||||
echo "" && \
|
||||
echo "🔍 Recherche de _team-content..." && \
|
||||
if [ -f "public/_team-content" ]; then \
|
||||
cp -v public/_team-content out/_team-content && echo "✅ _team-content copié depuis public/ vers out/"; \
|
||||
elif [ -f "/app/public/_team-content" ]; then \
|
||||
cp -v /app/public/_team-content out/_team-content && echo "✅ _team-content copié depuis /app/public/ vers out/"; \
|
||||
else \
|
||||
echo "⚠️ _team-content introuvable dans public/ ou /app/public/"; \
|
||||
echo "📁 Contenu de public/:" && \
|
||||
ls -la public/ 2>/dev/null | head -10 || echo "public/ non accessible"; \
|
||||
echo "📁 Contenu de /app/public/:" && \
|
||||
ls -la /app/public/ 2>/dev/null | head -10 || echo "/app/public/ non accessible"; \
|
||||
fi && \
|
||||
if [ -f "public/team-visualization-data.json" ]; then \
|
||||
cp -v public/team-visualization-data.json out/team-visualization-data.json && echo "✅ team-visualization-data.json copié dans out/"; \
|
||||
else \
|
||||
echo "⚠️ public/team-visualization-data.json introuvable"; \
|
||||
fi && \
|
||||
if [ -d "public/team" ]; then \
|
||||
mkdir -p out/team && \
|
||||
cp -rv public/team/* out/team/ && echo "✅ /team/index.html copié dans out/team/"; \
|
||||
elif [ -d "/app/radar-app/public/team" ]; then \
|
||||
mkdir -p out/team && \
|
||||
cp -rv /app/radar-app/public/team/* out/team/ && echo "✅ /team/index.html copié depuis /app/radar-app/public/team/"; \
|
||||
fi && \
|
||||
echo "🔍 VÉRIFICATION: _team-content dans out/:" && \
|
||||
ls -la out/_team-content 2>/dev/null || echo "❌ _team-content absent de out/" && \
|
||||
echo "" && \
|
||||
echo "📁 Vérification finale dans out/:" && \
|
||||
ls -la out/ | grep -E "(team\.html|team-visualization)" && echo "✅ Fichiers team présents dans out/" || echo "⚠️ Fichiers team non trouvés dans out/"; \
|
||||
else \
|
||||
echo "❌ Dossier out/ introuvable après build"; \
|
||||
ls -la . | head -20; \
|
||||
fi && \
|
||||
echo "" && \
|
||||
echo "📋 Vérification finale de Navigation.tsx après build:" && \
|
||||
grep -qE 'href="/team' src/components/Navigation/Navigation.tsx && echo "✅ Lien Équipe toujours présent dans Navigation.tsx après build" || echo "❌ Lien Équipe absent de Navigation.tsx après build" && \
|
||||
echo "" && \
|
||||
echo "🔍 Vérification des doublons dans le HTML généré..." && \
|
||||
if [ -f "out/index.html" ]; then \
|
||||
header_count=$(grep -oE '<header|<nav[^>]*>' out/index.html | wc -l | tr -d ' '); \
|
||||
nav_count=$(grep -oE '<nav[^>]*>' out/index.html | wc -l | tr -d ' '); \
|
||||
logo_count=$(grep -oE 'logo\.svg|logoFile|CoeurBox' out/index.html | wc -l | tr -d ' '); \
|
||||
echo "📊 HTML généré: $header_count header/nav, $nav_count nav, $logo_count logo"; \
|
||||
if [ "$header_count" -gt "2" ] || [ "$nav_count" -gt "2" ]; then \
|
||||
echo "❌ ERREUR: Duplication détectée dans le HTML généré!"; \
|
||||
echo "📄 Recherche des headers/nav dans index.html:"; \
|
||||
grep -nE '<header|<nav' out/index.html | head -10 || true; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "✅ HTML généré correct (pas de duplication structurelle)"; \
|
||||
fi; \
|
||||
if [ "$logo_count" -gt "5" ]; then \
|
||||
echo "⚠️ ATTENTION: $logo_count références au logo dans le HTML (possible duplication)"; \
|
||||
else \
|
||||
echo "✅ Références logo correctes dans le HTML"; \
|
||||
fi; \
|
||||
else \
|
||||
echo "⚠️ out/index.html non trouvé, vérification HTML ignorée"; \
|
||||
fi
|
||||
WORKDIR /app
|
||||
|
||||
# Exposition du port interne
|
||||
EXPOSE 3000
|
||||
|
||||
226
Readme.md
226
Readme.md
@@ -1,51 +1,177 @@
|
||||
# AJR Technology Radar - Content
|
||||
# Techradar Laplank
|
||||
|
||||
Ce dépôt contient le contenu du Technology Radar AJR, publié sous : https://www.coeurbox.syoul.fr
|
||||
**Techradar Laplank** est un Technology Radar interactif pour suivre l'évolution des technologies de l'écosystème Laplank/Duniter/Ğ1. Le projet est basé sur le framework [aoe_technology_radar](https://github.com/AOEpeople/aoe_technology_radar), dont le code source est vendu dans le répertoire `radar-app/`.
|
||||
|
||||
Le projet est basé sur le framework [aoe_technology_radar](https://github.com/AOEpeople/aoe_technology_radar).
|
||||
## 🎯 Vue d'ensemble
|
||||
|
||||
## Vue d'ensemble
|
||||
**Techradar Laplank** est un projet **stand-alone** qui présente :
|
||||
- **38 technologies** organisées par quadrants business et anneaux d'adoption
|
||||
- **12 membres d'équipe** avec leurs compétences et projets
|
||||
- **Visualisations interactives** : graphe réseau, matrice de congestion, équipe de genèse
|
||||
- **Pages de stratégie** : Technique, Business, DataViz Expert
|
||||
|
||||
Le projet contient deux radars distincts :
|
||||
### Technologies utilisées
|
||||
|
||||
1. **Radar Technique Principal** : Radar standard des technologies utilisées par AJR
|
||||
2. **Radar Business** : Radar stratégique business pour analyser les technologies de l'écosystème Laplank/Duniter/Ğ1
|
||||
- **Next.js** : 16.1.6 (avec Turbopack)
|
||||
- **React** : 19
|
||||
- **TypeScript** : 5
|
||||
- **Cytoscape.js** : Visualisation du graphe réseau
|
||||
- **ECharts** : Matrice de congestion
|
||||
- **Node.js** : 20+
|
||||
|
||||
## Radar Business
|
||||
## 🚀 Démarrage rapide
|
||||
|
||||
Le Radar Business est un outil stratégique accessible via le port **3004** avec une **protection par mot de passe** (`laplank-radar`).
|
||||
### Prérequis
|
||||
|
||||
### Fonctionnalités
|
||||
- Node.js 20 ou supérieur
|
||||
- npm ou yarn
|
||||
- Docker (pour le déploiement)
|
||||
|
||||
- **Pages de stratégie dynamiques** : Accès à trois pages de stratégie depuis le header :
|
||||
- Stratégie Technique
|
||||
- Business
|
||||
- DataViz Expert
|
||||
- **Protection par mot de passe** : Accès restreint via un système d'authentification client-side
|
||||
- **Quadrants business** : Classification selon l'impact business (Différenciantes, Commodité, Risque, Émergentes)
|
||||
- **Anneaux stratégiques** : Core, Strategic, Support, Legacy
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
git clone gitea@git.open.us.org:AJR/TechradarDev.git
|
||||
cd TechradarDev
|
||||
npm install
|
||||
```
|
||||
|
||||
### Développement local
|
||||
|
||||
**Mode développement Next.js** (avec hot-reload) :
|
||||
```bash
|
||||
npm run serve-dev
|
||||
```
|
||||
Le serveur démarre sur http://localhost:3000
|
||||
|
||||
**Mode production local** (serveur statique) :
|
||||
```bash
|
||||
npm install
|
||||
npm run serve-business
|
||||
```
|
||||
Le serveur démarre sur http://localhost:3006
|
||||
|
||||
Le serveur démarre sur http://localhost:3004
|
||||
**Build de production** :
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
Les fichiers statiques sont générés dans le répertoire `build/`
|
||||
|
||||
### Déploiement
|
||||
## 🐳 Déploiement Docker
|
||||
|
||||
Le Radar Business est déployé via Docker et Portainer :
|
||||
Le radar est déployé via Docker Compose :
|
||||
|
||||
**Commande de déploiement :**
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.business.yml up -d --build
|
||||
```
|
||||
|
||||
Cette commande :
|
||||
- Build l'image Docker sans cache (`--build`)
|
||||
- Démarre le conteneur en mode détaché (`-d`)
|
||||
- Le radar sera accessible sur http://localhost:3006
|
||||
|
||||
**Arrêter le déploiement :**
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.business.yml down
|
||||
```
|
||||
|
||||
**Configuration Docker :**
|
||||
- **Dockerfile** : `Dockerfile.business`
|
||||
- **Docker Compose** : `docker-compose.business.yml`
|
||||
- **Port** : 3004 (mappé depuis le port 3000 du conteneur)
|
||||
- **Base path** : `/` (racine)
|
||||
- **Port** : 3006 (mappé depuis le port 3000 du conteneur)
|
||||
- **Conteneur** : `laplank-radar-technolologique`
|
||||
|
||||
## Content Guidelines
|
||||
## 📊 Fonctionnalités
|
||||
|
||||
Les nouveaux blips doivent être tagués. Les tags suivants sont établis :
|
||||
### Radar Technologique
|
||||
|
||||
- **Quadrants business** :
|
||||
- Technologies Différenciantes
|
||||
- Technologies Commodité
|
||||
- Technologies Risque
|
||||
- Technologies Émergentes
|
||||
|
||||
- **Anneaux d'adoption** :
|
||||
- **Adopt** : Technologies adoptées et utilisées en production
|
||||
- **Trial** : Technologies en phase d'essai
|
||||
- **Assess** : Technologies à évaluer
|
||||
- **Hold** : Technologies à éviter ou remplacer
|
||||
|
||||
- **38 technologies** suivies dans la release 2025-01-15
|
||||
|
||||
### Page Équipe (`/team`)
|
||||
|
||||
Visualisations interactives de l'équipe :
|
||||
|
||||
1. **Graphe réseau** : Relations entre membres et technologies (Cytoscape.js)
|
||||
2. **Matrice de congestion** : Technologies core vs disponibilité de l'équipe (ECharts)
|
||||
3. **Équipe de genèse** : Membres fondateurs et leurs compétences
|
||||
4. **Profils cliquables** : Cartes de profil détaillées pour chaque membre
|
||||
|
||||
**12 membres** de l'équipe avec leurs compétences, projets et disponibilités.
|
||||
|
||||
### Pages de stratégie
|
||||
|
||||
Accès à trois pages de stratégie depuis le header :
|
||||
- **Stratégie Technique** : Vision technique et roadmap
|
||||
- **Business** : Impact business et métriques
|
||||
- **DataViz Expert** : Stratégie de visualisation de données
|
||||
|
||||
**Protection par mot de passe** : `laplank-radar`
|
||||
|
||||
## 📁 Structure du projet
|
||||
|
||||
```
|
||||
TechradarDev/
|
||||
├── radar-business/ # ✏️ SOURCE DE VÉRITÉ — éditer ici
|
||||
│ ├── 2025-01-15/ # Fichiers .md des technologies (38 blips)
|
||||
│ ├── config-business.json # Configuration du radar (quadrants, rings)
|
||||
│ └── FORMAT-BLIP.md # Format des métadonnées
|
||||
├── data/ # ✏️ Données de build versionnées
|
||||
│ └── team/ # Profils des 12 membres (*.md)
|
||||
├── radar-app/ # ⚙️ Framework Next.js (ne pas éditer)
|
||||
│ ├── src/ # Code source Next.js
|
||||
│ └── data/ # Copie générée au build (ne pas éditer)
|
||||
├── radar/ # ⚠️ Dossier temporaire (généré par serve-business.sh)
|
||||
│ └── ... # Ne jamais éditer ici — écrasé à chaque lancement
|
||||
├── scripts/
|
||||
│ ├── build-radar.js # Script de build stand-alone
|
||||
│ ├── serve-radar.js # Script de serve
|
||||
│ ├── generate-team-visualization-data.js
|
||||
│ └── ...
|
||||
├── public/ # Fichiers statiques
|
||||
│ ├── team-block-script.js
|
||||
│ └── team-visualization-data.json # Généré par generate-team-visualization-data.js
|
||||
├── Dockerfile.business
|
||||
└── docker-compose.business.yml
|
||||
```
|
||||
|
||||
## 🛠️ Scripts disponibles
|
||||
|
||||
| Script | Description |
|
||||
|--------|-------------|
|
||||
| `npm run build` | Build de production (génère `build/`) |
|
||||
| `npm run serve` | Serve les fichiers statiques depuis `build/` |
|
||||
| `npm run serve-dev` | Mode développement Next.js (hot-reload) |
|
||||
| `npm run serve-business` | Serve le radar business en local (port 3006) |
|
||||
| `npm run extract-tech` | Extraction des technologies depuis la doc |
|
||||
| `npm run analyze-business` | Analyse des métriques business |
|
||||
|
||||
## ✏️ Où éditer le contenu
|
||||
|
||||
| Quoi | Dossier |
|
||||
|---|---|
|
||||
| Ajouter / modifier une technologie | `radar-business/2025-01-15/` |
|
||||
| Ajouter / modifier un membre d'équipe | `data/team/` |
|
||||
|
||||
> ⚠️ Ne jamais éditer dans `radar/` (temporaire, écrasé au lancement) ni dans `radar-app/data/` (copie générée au build).
|
||||
|
||||
## 📝 Content Guidelines
|
||||
|
||||
### Tags disponibles
|
||||
|
||||
Les blips doivent être tagués avec les tags suivants :
|
||||
|
||||
* architecture
|
||||
* security
|
||||
@@ -58,35 +184,39 @@ Les nouveaux blips doivent être tagués. Les tags suivants sont établis :
|
||||
* ux/ui
|
||||
* documentation
|
||||
|
||||
Exemple d'utilisation :
|
||||
|
||||
```md
|
||||
Exemple :
|
||||
```markdown
|
||||
tags: [devops, security]
|
||||
```
|
||||
|
||||
## Development
|
||||
### Format des blips
|
||||
|
||||
### Build le radar principal
|
||||
```
|
||||
npm install
|
||||
npm run serve
|
||||
```
|
||||
Voir `radar-business/FORMAT-BLIP.md` pour le format complet des métadonnées business.
|
||||
|
||||
Puis ouvrir : http://localhost:3000/techradar
|
||||
## 🔐 Sécurité
|
||||
|
||||
### Build avec fichiers statiques
|
||||
```
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
- **Protection par mot de passe** : Les pages de stratégie sont protégées par le mot de passe `laplank-radar`
|
||||
- **Authentification client-side** : Système d'authentification JavaScript côté client
|
||||
|
||||
## Documentation
|
||||
## 📚 Documentation
|
||||
|
||||
La documentation complète est disponible dans le dossier `docs/` :
|
||||
La documentation technique est disponible dans `docs/app/` (non versionnée, locale uniquement) :
|
||||
|
||||
- [Architecture](./docs/architecture.md)
|
||||
- [Configuration](./docs/configuration.md)
|
||||
- [Développement](./docs/developpement.md)
|
||||
- [Déploiement](./docs/deploiement.md)
|
||||
- [Contribution](./docs/contribution.md)
|
||||
- [Guide Radar Business](./docs/guide-radar-business.md)
|
||||
- Architecture, Configuration, Développement, Déploiement
|
||||
- Guide Radar Business, Guide Page Équipe, Migration Next.js 16
|
||||
|
||||
## 🔄 État du projet
|
||||
|
||||
- **Branche principale** : `main`
|
||||
- **Version** : 4.3.0
|
||||
- **Statut** : Projet stand-alone, indépendant du package externe `aoe_technology_radar`
|
||||
- **Framework** : Code vendu dans `radar-app/` (basé sur aoe_technology_radar)
|
||||
|
||||
## 📞 Support
|
||||
|
||||
- **Dépôt Git** : https://git.open.us.org/AJR/TechradarDev
|
||||
- **Radar en ligne** : http://localhost:3006 (après déploiement)
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
MIT
|
||||
|
||||
85
about.md
85
about.md
@@ -1,79 +1,44 @@
|
||||
# How to use the AOE Technology Radar
|
||||
# Comment utiliser le Radar Technologique Laplank
|
||||
|
||||
### Introduction
|
||||
|
||||
Technology is advancing rapidly, with new technologies and innovations constantly emerging.
|
||||
Le monde de la technologie évolue à une vitesse fulgurante. Pour une organisation comme Laplank, il est crucial de rester à la pointe, d'évaluer constamment les nouvelles innovations et de remettre en question les technologies établies. Ce radar est un outil pour nous aider dans cette démarche.
|
||||
|
||||
It is essential for a development and technology company like AOE to continually improve and keep
|
||||
track of the latest valuable innovations. It is important to actively seek out innovations and new
|
||||
technologies and periodically question established technologies and methods.
|
||||
### Qu'est-ce que le Radar Technologique Laplank ?
|
||||
|
||||
But, it is also important to wisely choose which technologies to use in our daily work and in the
|
||||
different projects we are carrying out. As we all know: There is no silver bullet.
|
||||
Le Radar Technologique Laplank offre une vue d'ensemble des technologies (langages, frameworks, outils, méthodes, plateformes) que nous considérons pertinentes pour notre écosystème, en particulier autour de la monnaie libre Ğ1 et de l'infrastructure décentralisée. Il se concentre sur les éléments qui ont récemment gagné en importance ou qui ont subi des changements significatifs.
|
||||
|
||||
### What is the AOE Technology Radar?
|
||||
### Comment est-il créé ?
|
||||
|
||||
The Tech Radar provides an overview of different technologies, including languages, frameworks,
|
||||
tools, and patterns, as well as platforms, that we consider 'new or noteworthy.' The radar does not
|
||||
cover all established technologies; instead, it focuses on items that have recently gained
|
||||
significance or undergone changes. Items previously featured in the radar are not listed on the
|
||||
homepage but remain available in the complete overview and search.
|
||||
Les éléments du radar sont suggérés par les membres de l'équipe, souvent en lien avec les défis rencontrés dans nos projets. Chaque technologie est évaluée et classifiée après des discussions approfondies au sein des groupes d'experts. Nous n'incluons que des technologies que nous avons personnellement testées ou étudiées.
|
||||
|
||||
### How it is created
|
||||
### Comment l'utiliser ?
|
||||
|
||||
The items in the technology radar are suggested by different teams, many of which are related to the
|
||||
work and challenges faced by the teams in various projects. In fact, we do not include anything on
|
||||
the radar that we haven't personally tested at least once.
|
||||
Le radar sert de guide et d'inspiration pour le travail quotidien des équipes. Il vise à fournir une perspective de haut niveau pour prendre des décisions éclairées et coordonnées.
|
||||
|
||||
Numerous valuable discussions have taken place in various expert groups regarding the classification
|
||||
and details of each technology and innovation. The culmination of these discussions is reflected in
|
||||
the latest technology radar.
|
||||
Nous classifions les éléments en quatre quadrants et quatre anneaux.
|
||||
|
||||
### How should it be used
|
||||
#### Les quadrants sont :
|
||||
|
||||
The radar serves as an overview of technologies that we believe everyone in the teams should be
|
||||
aware of at present.
|
||||
- **Technologies Différenciantes** : Technologies qui créent un avantage concurrentiel et de la valeur différenciante pour le business. Ce sont les technologies qui nous permettent de nous distinguer sur le marché.
|
||||
- **Technologies de Commodité** : Technologies nécessaires mais non différenciantes. Elles sont essentielles au fonctionnement mais ne créent pas d'avantage concurrentiel direct. L'objectif est de les optimiser pour réduire les coûts et la complexité.
|
||||
- **Technologies à Risque** : Technologies obsolètes, coûteuses ou présentant des risques techniques ou business importants. Il est recommandé de les migrer ou de les remplacer pour réduire les risques et les coûts.
|
||||
- **Technologies Émergentes** : Technologies prometteuses représentant des opportunités futures. Elles sont à évaluer et potentiellement à adopter pour créer de nouveaux avantages compétitifs.
|
||||
|
||||
Its goal is to guide and inspire daily work within the teams. Additionally, it aims to provide
|
||||
valuable information and a high-level perspective to enable decisions to be made with a deeper
|
||||
understanding of the subject matter, resulting in more informed and coordinated choices.
|
||||
#### Chaque élément est classé dans l'un de ces anneaux :
|
||||
|
||||
We also hope that developers outside of AOE will find the information in our technology overview
|
||||
inspiring.
|
||||
- **Adopt** : Nous recommandons pleinement cette technologie. Elle a été largement utilisée avec succès dans nos projets, prouvant sa stabilité, son utilité et sa maturité. Elle peut être adoptée en toute confiance pour de nouveaux projets.
|
||||
*Exemples : Rust, Substrate Framework, Vue.js, JavaScript/TypeScript, Flutter, Dart, GitLab CI/CD, Linux, Docker, ThreeFold Blockchain.*
|
||||
|
||||
We categorize the items into four quadrants, and sometimes, when it's not entirely clear where an
|
||||
item belongs, we choose the best fit.
|
||||
- **Trial** : Nous avons mis en œuvre cette technologie avec succès dans des contextes spécifiques et suggérons de l'examiner de plus près. L'objectif est de la tester davantage avec l'intention de l'élever au niveau 'Adopt' si elle confirme son potentiel.
|
||||
*Exemples : IPFS, Nostr, ThreeFold Grid, ThreeFold Cloud/Compute/Storage, D3.js, ECharts, Cytoscape.js, Grafana, Leaflet, Zero OS, Mycelium Network, AIBox, Ansible, OpenTofu.*
|
||||
|
||||
#### The quadrants are:
|
||||
- **Assess** : Nous avons expérimenté cette technologie et la trouvons prometteuse. Nous recommandons de l'explorer lorsque vous rencontrez un besoin spécifique pour cette technologie dans votre projet. Une évaluation approfondie est nécessaire avant toute adoption.
|
||||
*Exemples : Nuxt.js, PostgreSQL, Python, ProxMox, Cryptographie, Bash, Serverless, Kubernetes, NetlifyCMS, WordUp CMS, Squid.*
|
||||
|
||||
- **Languages & Frameworks:** In this category, we include development languages like Scala or
|
||||
Golang, as well as low-level development frameworks such as Play or Symfony. These are valuable
|
||||
for implementing various types of custom software.
|
||||
- **Tools:** This section is dedicated to a wide range of software tools, from small utilities to
|
||||
more extensive software projects.
|
||||
- **Methods & Patterns:** Patterns hold enduring significance, with many of them standing the test
|
||||
of time compared to some tools or frameworks. This category is where we provide information on
|
||||
methods and patterns related to development, continuous integration, testing, organization,
|
||||
architecture, and more.
|
||||
- **Platforms & Operations:** In this quadrant, we group technologies related to the operation of
|
||||
software, infrastructure, and platform-related tools and services.
|
||||
- **Hold** : Cette catégorie est unique. Elle conseille d'arrêter ou de s'abstenir d'utiliser certaines technologies pour de nouveaux projets. Cela n'implique pas nécessairement qu'elles sont intrinsèquement mauvaises, mais nous avons identifié de meilleures options ou alternatives, ou elles présentent des risques significatifs.
|
||||
*Exemples : (Actuellement aucune technologie n'est en 'Hold' dans ce radar, mais cela pourrait inclure des technologies obsolètes ou non maintenues).*
|
||||
|
||||
#### Each of the items is classified in one of these rings:
|
||||
### Contribuer au Radar Technologique Laplank
|
||||
|
||||
- **Adopt:** We wholeheartedly recommend this technology. It has been extensively used in many teams
|
||||
for an extended period, proving its stability and utility.
|
||||
- **Trial:** We have successfully implemented this technology and suggest taking a closer look at it
|
||||
in this category. The aim here is to scrutinize these items more closely with the intention of
|
||||
elevating them to the 'Adopt' level.
|
||||
- **Assess:** We have experimented with this technology and find it promising. We recommend
|
||||
exploring these items when you encounter a specific need for the technology in your project.
|
||||
- **Hold:** This category is somewhat unique. Unlike the others, it advises discontinuing or
|
||||
refraining from using certain technologies. This does not necessarily imply that they are
|
||||
inherently bad; it often may be acceptable to use them in existing projects. However, we move
|
||||
items here when we believe they should no longer be employed, as we have identified better options
|
||||
or alternatives.
|
||||
|
||||
### Contributing to the AOE Technology Radar
|
||||
|
||||
Contributions and source code of the AOE Tech Radar are on
|
||||
GitHub: [AOE Tech Radar on GitHub](https://github.com/AOEpeople/aoe_technology_radar)
|
||||
Les contributions et le code source du Radar Technologique Laplank sont disponibles sur notre dépôt Git.
|
||||
|
||||
38
config.json
38
config.json
@@ -3,7 +3,7 @@
|
||||
"baseUrl": "",
|
||||
"editUrl": "https://git.open.us.org/syoul/TechradarDev/_edit/main/radar-business/{release}/{id}.md",
|
||||
"logoFile": "logo.svg",
|
||||
"jsFile": "strategie-script.js",
|
||||
"jsFile": "/team-block-script.js",
|
||||
"toggles": {
|
||||
"showChart": true,
|
||||
"showTagFilter": true,
|
||||
@@ -49,34 +49,34 @@
|
||||
],
|
||||
"rings": [
|
||||
{
|
||||
"id": "core",
|
||||
"title": "Core",
|
||||
"description": "Technologies critiques pour le business model. Indispensables au fonctionnement et à la création de valeur. Investissement prioritaire en maintenance et évolution.",
|
||||
"id": "adopt",
|
||||
"title": "Adopt",
|
||||
"description": "Technologies recommandées et utilisées avec succès en production. Elles sont stables, éprouvées et peuvent être adoptées en toute confiance pour de nouveaux projets. Utilisation active dans l'écosystème Duniter/Ğ1.",
|
||||
"color": "#27ae60",
|
||||
"radius": 0.5,
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"id": "strategic",
|
||||
"title": "Strategic",
|
||||
"description": "Technologies stratégiques pour la croissance et le développement. Investissements importants pour renforcer la position concurrentielle.",
|
||||
"id": "trial",
|
||||
"title": "Trial",
|
||||
"description": "Technologies à essayer. Elles sont prometteuses et ont été testées avec succès dans certains contextes. À considérer pour de nouveaux projets. Technologies en phase d'expérimentation active.",
|
||||
"color": "#3498db",
|
||||
"radius": 0.69,
|
||||
"strokeWidth": 4
|
||||
},
|
||||
{
|
||||
"id": "support",
|
||||
"title": "Support",
|
||||
"description": "Technologies de support nécessaires mais non critiques. À maintenir à un niveau fonctionnel sans sur-investissement.",
|
||||
"color": "#95a5a6",
|
||||
"id": "assess",
|
||||
"title": "Assess",
|
||||
"description": "Technologies à évaluer. Elles sont prometteuses mais nécessitent une évaluation approfondie avant adoption. À surveiller et tester. Technologies nécessitant une analyse plus poussée.",
|
||||
"color": "#f39c12",
|
||||
"radius": 0.85,
|
||||
"strokeWidth": 3
|
||||
},
|
||||
{
|
||||
"id": "legacy",
|
||||
"title": "Legacy",
|
||||
"description": "Technologies à remplacer. Présentent des risques techniques, des coûts élevés ou sont obsolètes. Planifier la migration vers des alternatives modernes.",
|
||||
"color": "#c0392b",
|
||||
"id": "hold",
|
||||
"title": "Hold",
|
||||
"description": "Technologies à éviter ou à remplacer. Elles présentent des risques, sont obsolètes ou ne sont plus recommandées. À éviter pour de nouveaux projets. Technologies à migrer ou remplacer.",
|
||||
"color": "#e74c3c",
|
||||
"radius": 1,
|
||||
"strokeWidth": 2
|
||||
}
|
||||
@@ -105,20 +105,20 @@
|
||||
"social": [],
|
||||
"imprint": "",
|
||||
"labels": {
|
||||
"title": "Radar Stratégique Business - Laplank",
|
||||
"title": "Radar Technologique Laplank",
|
||||
"imprint": "Informations légales",
|
||||
"quadrant": "Quadrant",
|
||||
"quadrantOverview": "Vue d'ensemble des quadrants",
|
||||
"zoomIn": "Zoomer",
|
||||
"filterByTag": "Filtrer par tag",
|
||||
"footer": "Radar stratégique pour analyser les technologies de l'écosystème Laplank et définir une stratégie d'évolution technique alignée avec les objectifs business.",
|
||||
"footer": "Radar technologique pour suivre l'évolution des technologies de l'écosystème Laplank avec historique des versions.",
|
||||
"notUpdated": "Cet élément n'a pas été mis à jour dans les trois dernières versions du Radar.",
|
||||
"notFound": "404 - Page non trouvée",
|
||||
"pageAbout": "Comment utiliser le Radar Business ?",
|
||||
"pageAbout": "Comment utiliser le Radar Technologique ?",
|
||||
"pageOverview": "Vue d'ensemble des technologies",
|
||||
"pageSearch": "Recherche",
|
||||
"searchPlaceholder": "Que recherchez-vous ?",
|
||||
"metaDescription": "Radar stratégique business pour l'écosystème Duniter/Ğ1 - Analyse des technologies et définition de la stratégie d'évolution."
|
||||
"metaDescription": "Radar technologique pour l'écosystème Laplank - Suivi de l'évolution des technologies avec historique."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
143
custom.css
143
custom.css
@@ -1,2 +1,145 @@
|
||||
/* Use this file to optionally override global css styles and use with caution. */
|
||||
/* See README.md for hints and examples: https://github.com/AOEpeople/aoe_technology_radar/ */
|
||||
|
||||
/* Améliorer la visibilité des icônes de navigation */
|
||||
nav[class*="Navigation"] svg,
|
||||
nav svg,
|
||||
header nav svg {
|
||||
fill: #2ecc71 !important;
|
||||
width: 22px !important;
|
||||
height: 22px !important;
|
||||
display: inline-block !important;
|
||||
vertical-align: middle !important;
|
||||
margin-right: 6px !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
/* S'assurer que les liens de navigation sont visibles */
|
||||
nav[class*="Navigation"] a,
|
||||
nav a,
|
||||
header nav a {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
color: #fff !important;
|
||||
text-decoration: none !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
nav[class*="Navigation"] a:hover,
|
||||
nav a:hover,
|
||||
header nav a:hover {
|
||||
color: #2ecc71 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* CORRECTION RADICALE DES LABELS DE QUADRANTS */
|
||||
/* Labels compacts mais lisibles, très éloignés des cercles */
|
||||
[class*="Radar_radar"] [class*="Label_label"],
|
||||
[class*="Label_label"] {
|
||||
width: 180px !important;
|
||||
max-width: 180px !important;
|
||||
min-height: auto !important;
|
||||
padding: 12px !important;
|
||||
background: rgba(26, 77, 58, 0.95) !important;
|
||||
border-radius: 8px !important;
|
||||
backdrop-filter: blur(4px) !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
/* Taille de texte lisible */
|
||||
[class*="Label_label"] [class*="title"],
|
||||
[class*="Label_label"] h3 {
|
||||
font-size: 15px !important;
|
||||
margin: 0 0 8px 0 !important;
|
||||
line-height: 1.3 !important;
|
||||
}
|
||||
|
||||
[class*="Label_label"] [class*="description"],
|
||||
[class*="Label_label"] p {
|
||||
font-size: 12px !important;
|
||||
line-height: 1.4 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
[class*="Label_label"] [class*="header"] {
|
||||
font-size: 11px !important;
|
||||
padding: 8px 0 !important;
|
||||
margin: 0 0 10px !important;
|
||||
}
|
||||
|
||||
/* Quadrant 1 (en haut à gauche) - TRÈS ÉLOIGNÉ */
|
||||
[class*="Radar_radar"] [class*="Label_label"][class*="Label_position-1"],
|
||||
[class*="Label_position-1"] {
|
||||
left: 10px !important;
|
||||
top: 10px !important;
|
||||
}
|
||||
|
||||
/* Quadrant 2 (en haut à droite) - TRÈS ÉLOIGNÉ */
|
||||
[class*="Radar_radar"] [class*="Label_label"][class*="Label_position-2"],
|
||||
[class*="Label_position-2"] {
|
||||
right: 10px !important;
|
||||
top: 10px !important;
|
||||
left: auto !important;
|
||||
}
|
||||
|
||||
/* Quadrant 3 (en bas à gauche) - TRÈS ÉLOIGNÉ */
|
||||
[class*="Radar_radar"] [class*="Label_label"][class*="Label_position-3"],
|
||||
[class*="Label_position-3"] {
|
||||
left: 10px !important;
|
||||
bottom: 10px !important;
|
||||
top: auto !important;
|
||||
}
|
||||
|
||||
/* Quadrant 4 (en bas à droite) - DESCENDU tout en bas */
|
||||
[class*="Radar_radar"] [class*="Label_label"][class*="Label_position-4"],
|
||||
[class*="Label_position-4"] {
|
||||
right: 10px !important;
|
||||
bottom: 10px !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
}
|
||||
|
||||
/* Ajuster la légende - positionnée tout en bas à droite */
|
||||
[class*="Radar_radar"] [class*="Legend_legend"],
|
||||
[class*="Legend_legend"] {
|
||||
z-index: 100 !important;
|
||||
padding: 12px 16px !important;
|
||||
background: rgba(26, 77, 58, 0.95) !important;
|
||||
border-radius: 8px !important;
|
||||
backdrop-filter: blur(4px) !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
|
||||
max-width: 180px !important;
|
||||
position: absolute !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
[class*="Legend_legend"] li {
|
||||
font-size: 12px !important;
|
||||
line-height: 1.5 !important;
|
||||
margin-bottom: 6px !important;
|
||||
}
|
||||
|
||||
/* Sur les grands écrans, positionner la légende ENCORE PLUS HAUT */
|
||||
@media (min-width: 1200px) {
|
||||
[class*="Radar_radar"] [class*="Legend_legend"],
|
||||
[class*="Legend_legend"] {
|
||||
right: 10px !important;
|
||||
bottom: 320px !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sur les écrans moyens, centrer la légende ENCORE PLUS HAUT */
|
||||
@media (min-width: 768px) and (max-width: 1199px) {
|
||||
[class*="Radar_radar"] [class*="Legend_legend"],
|
||||
[class*="Legend_legend"] {
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
transform: translateX(-50%) !important;
|
||||
bottom: 320px !important;
|
||||
top: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<h1 style="color: #1a4d3a; border-bottom: 3px solid #2ecc71; padding-bottom: 10px;">Stratégie d'Évolution Technique - Laplank</h1>
|
||||
<p><strong>Date de mise à jour</strong> : 02/12/2025</p>
|
||||
<p>La stratégie complète est disponible dans le dépôt Git :</p>
|
||||
<p><a href="https://git.open.us.org/AJR/TechradarDev/-/blob/dev-biz/docs/strategie-evolution-technique.md" target="_blank" style="color: #2ecc71; font-weight: bold;">📋 Voir la stratégie sur GitLab</a></p>
|
||||
<p><a href="https://git.open.us.org/AJR/TechradarDev/-/blob/dev-biz/docs/data/strategie-evolution-technique.md" target="_blank" style="color: #2ecc71; font-weight: bold;">📋 Voir la stratégie sur GitLab</a></p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
15
data/README.md
Normal file
15
data/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Données du Radar Technologique Laplank
|
||||
|
||||
Ce dossier contient les données utilisées par l'application Radar Technologique Laplank.
|
||||
|
||||
## Contenu
|
||||
|
||||
- **technologies-duniter.md** : Liste des technologies de l'écosystème Duniter/Ğ1
|
||||
- **profil-team.md** : Profils et compétences des membres de l'équipe
|
||||
- **strategie-evolution-technique.md** : Vision et roadmap technique
|
||||
- **strategie-business.md** : Analyse stratégique business
|
||||
- **opportunites-dataviz.md** : Opportunités en dataviz
|
||||
- **opportunites-dataviz-details.md** : Détails des opportunités dataviz
|
||||
- **analyse-strategique.md** : Rapport d'analyse généré automatiquement
|
||||
|
||||
Ces fichiers sont utilisés par les scripts et l'application pour générer le contenu du radar.
|
||||
@@ -1,4 +1,4 @@
|
||||
poka
|
||||
# poka
|
||||
|
||||
Je suis contributeur actif sur le projet Duniter depuis 2016 aux RML7 de Laval.
|
||||
|
||||
@@ -6,12 +6,8 @@ Je code Ğecko en Flutter/Dart. Je maintiens aussi l’infra Axiom-Team, soit 2
|
||||
|
||||
J’ai aussi codé Ğ1-stats en bash. Et jaklis en python. J’ai aussi codé py-g1-migrator
|
||||
|
||||
J’aime la soupe au poireaux.
|
||||
|
||||
edit important: J’aime aussi les tartes aux légumes. Je préfère parler de tarte plutôt que de quiche aux légumes, même si j’aime beaucoup les quiches aussi. Je n’ai aucune animosité particulière vie-à-vis des gens qui préfèrent parler de quiche aux légumes plutôt que de tartes, même si je les considères comme éloignés de moi (au niveau de la race je veux dire hein, pas de malentendu).
|
||||
|
||||
Je n’aime pas le cresson.
|
||||
ManUtopiK
|
||||
# ManUtopiK
|
||||
|
||||
Diplomé dans le domaine des énergies renouvelables, mon côté “web enthousiaste” m’a finalement amené à faire du développement web depuis + de 12 ans.
|
||||
|
||||
@@ -32,29 +28,31 @@ En cours
|
||||
Extension web g1Compagnon
|
||||
Interface web pour g1Billet
|
||||
|
||||
Hugo Trentesaux
|
||||
# Hugo Trentesaux
|
||||
|
||||
Je m’intéresse à la Ğ1 depuis 2017 et pense que l’association Axiom Team constitue une base juridique utile car nécessaire pour de nombreuses interactions avec le monde €.
|
||||
|
||||
J’ai travaillé sur le dossier de financement de Ǧecko auprès de l’ADEME avec succès. À l’avenir, je compte participer au fonctionnement d’Axiom Team, et à la partie rédactionnelle des dossiers de financement.
|
||||
aya
|
||||
|
||||
# aya
|
||||
|
||||
Je participe à la vulgarisation des logiciels libres depuis ma première installation de linux debian potato en 2001.
|
||||
|
||||
J’ai découvert la monnaie libre à travers mes recherches concernant les systèmes de fichiers. Travaillant principalement sur des infrastructures d’hébergement distribué, j’ai utilisé différents systèmes de réplication de fichiers comme glusterfs, cephfs, pour en arriver à ipfs. C’est en cherchant une alternative à filecoin, la crypto proposée par ipfs pour mettre en commun son espace de stockage, que je découvre la monnaie libre, on est en 2021.
|
||||
|
||||
Je rejoins Axiom-Team pour participer à la vulgarisation de la monnaie libre.
|
||||
Syoul
|
||||
|
||||
Actuellement secrétaire d’Axiom-Team
|
||||
# Syoul
|
||||
|
||||
Artisan bidouilleur Libriste, formé à la bidouille (résilience numérique, énergétique, domotique). Artisan laser numérique sur le causse du Querçy (46)
|
||||
|
||||
J’ai découvert la June en 2018. Depuis, j’anime avec des groupes locaux, des conférences et Ğmarchés autour de la monnaie libre G1.
|
||||
Eloïs
|
||||
|
||||
# Eloïs
|
||||
|
||||
A appris les technologies blockchain en autodidact, travaillé sur la “rustification” (passage en Rust) de Duniter v1, puis bossé chez MoonPay.
|
||||
Yvv
|
||||
|
||||
# Yvv
|
||||
|
||||
Vieux bouc dans le CA, je tire ma révérence en tant que secrétaire. Focus sur ce qui m’intéresse le plus, nouvelle forme de mobilisation.
|
||||
|
||||
@@ -74,19 +72,22 @@ Pour ML :
|
||||
Lancer un événement structurant, le Librodrome.
|
||||
Lancer une expérience de production collective monnaie-libriste, probablement une conserverie éphémère mobile.
|
||||
|
||||
Fred
|
||||
# Fred
|
||||
|
||||
A monté une boite (Linkeo) qui a bouffé une partie du marché de PagesJaunes début/milieu des années 2000. Très intéressé (et sachant) sur IPFS, Secure ScuttleButt, Nostr et TiddlyWiki. Il développe Astroport, un système d’information qui combine la Ğ1, IPFS et Nostr. Par le passé, il a aussi créé G1SMS (système de paiement par SMS en Ğ1) et G1billet (paper wallet pour la Ğ1).
|
||||
Vivien
|
||||
|
||||
# Vivien
|
||||
|
||||
Se forme pour contribuer à certains logiciels de la Ğ1 (Cesium). Développe aussi en Godot. Passionné de jeux (cartes Magic notamment).
|
||||
1000i100
|
||||
|
||||
# 1000i100
|
||||
|
||||
Développeur d’outils serverless, et plombier des pipeline Gitlab (CI/CD avec Docker). Enfin une monnaie mécaniquement redistributive ! Avec un soupçon de revenu de base, une bonne dose d’auto-gestion et une communauté adorable !Informaticien couteau suisse à dominante développeur web, photographe à ses heures, soutien psy informel, amateur de CNV et de modèles économiques expérimental et éthique !
|
||||
tuxmain
|
||||
|
||||
# tuxmain
|
||||
|
||||
Étudiant en math. Bien compétent sur la cryptographie, le chiffrage, les conversions de clef d’une base en une autre. Administrateur de serveur Minetest. Il bidouille aussi de l’électronique.
|
||||
|
||||
boris
|
||||
# boris
|
||||
|
||||
Il est assez dispersé, “jack of all trade, master of none”. Ces derniers temps, il passe beaucoup de temps à faire de la génération de musiques rigolotes (ou autre) avec les LLM et Suno. Il aime les langues étrangères (l’anglais surtout), la médecine traditionnelle chinoise, le Feng Shui (le tao en général). Il est communiste. Il a bossé sur l’UX/UI de Ğecko (via Figma). Grâce à Cursor, il développe une app de médecine chinoise basée sur les LLM. Dans la Ğ1, il a essayé de contribuer à l’onboarding (il a refait le site monnaie-libre.fr, Duniter | Accueil, et fait le site cesium.app). Il a aussi fait des clients Ğchange : Ğ1Quest (une projection des annonces Ğchange, notamment en “vue radar”), Ğrocéliande (un genre de skin pour Ğchange calqué sur l’interface d’Amazon, et qui ne prend que les annonces avec “envoi possible” dans la description), g1.business (qui permet de repérer les “routes commerciale”, de faire correspondre pour un produit l’offre d’un endroit et la demande à un endroit distant, et qui projette sur une carte les moyens de productions disponibles à la location en Ğ1). Il a aussi fait Ğ1Gate (qui permet de suivre les flux de monnaie en vue “treemap”), H2G2 “le guide du terraformeur terrien” (une vue à la recette MineCraft de choses qu’on peut produire “dans la vraie vie”), Ğ1 KDE Notifier (Un petit outil pour être notifié de mouvements sur un portefeuille Ğ1), un Simulateur RSA / Prime d’activité (Un simulateur RSA/prime d’activité plus très à jour au niveau des données, mais qui permet de se rendre compte à quel point le fonctionnement de la prime d’activité est complètement stupide, et incite à éviter de travailler de façon trop importante trop ponctuellement, si on ne veut pas risquer de perdre de l’argent en allant se casser le cul au boulot), Cerveau externe (Un truc fait avec Vis.js, pour projeter des mots, colorés suivant la rime, regroupés autour des consonnes, et liés s’ils appartiennent à un même thème. Dans l’idée de faire des impros de rap avec. Proto sans réelle interface utilisateur utilisable par les moldus. Faire F5 pour raffraîchir et ainsi avoir un autre graphe de mots.), NoBS Troll-Emploi (Un moteur de recherche d’emploi basé sur l’API Pôle-Emploi et qui permet d’avoir plus de filtres : mots-clefs à exclure, pas de tutoiement, pas de “digital”, etc… Idéal pour les gens qui, certes, acceptent d’être exploités lorsqu’ils développent du logiciel, mais veulent diminuer au maximum la quantité de bullshit dans leur job).
|
||||
42
data/team/1000i100.md
Normal file
42
data/team/1000i100.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: "1000i100"
|
||||
fullName: "1000i100"
|
||||
role: "DevOps & Développeur Web"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 10
|
||||
joinDate: "2018-01"
|
||||
interests: ["Serverless", "CI/CD", "Docker", "Photographie", "CNV", "Modèles économiques"]
|
||||
skills:
|
||||
- name: "Serverless"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "GitLab"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "CI/CD"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "Docker"
|
||||
level: expert
|
||||
years: 7
|
||||
lastUsed: "2024-12"
|
||||
- name: "web"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Polyvalence"
|
||||
- "Photographie"
|
||||
- "Soutien psychologique"
|
||||
- "CNV (Communication Non Violente)"
|
||||
projects:
|
||||
- "Outils serverless"
|
||||
- "Pipeline GitLab CI/CD"
|
||||
---
|
||||
|
||||
Développeur d'outils serverless, et plombier des pipeline Gitlab (CI/CD avec Docker). Enfin une monnaie mécaniquement redistributive ! Avec un soupçon de revenu de base, une bonne dose d'auto-gestion et une communauté adorable ! Informaticien couteau suisse à dominante développeur web, photographe à ses heures, soutien psy informel, amateur de CNV et de modèles économiques expérimental et éthique !
|
||||
|
||||
52
data/team/aya.md
Normal file
52
data/team/aya.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: "aya"
|
||||
fullName: "aya"
|
||||
role: "Administrateur Système & Infrastructure Distribuée"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 23
|
||||
joinDate: "2021-01"
|
||||
interests: ["Logiciels libres", "Infrastructure distribuée", "Stockage distribué", "IPFS", "ThreeFold"]
|
||||
skills:
|
||||
- name: "Linux"
|
||||
level: expert
|
||||
years: 23
|
||||
lastUsed: "2024-12"
|
||||
- name: "glusterfs"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2023-06"
|
||||
- name: "cephfs"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2023-06"
|
||||
- name: "ipfs"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "infrastructure"
|
||||
level: expert
|
||||
years: 15
|
||||
lastUsed: "2024-12"
|
||||
- name: "systèmes distribués"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
- name: "ThreeFold"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Vulgarisation"
|
||||
- "Autonomie"
|
||||
- "Recherche"
|
||||
projects:
|
||||
- "Infrastructure d'hébergement distribué"
|
||||
---
|
||||
|
||||
Je participe à la vulgarisation des logiciels libres depuis ma première installation de linux debian potato en 2001.
|
||||
|
||||
J'ai découvert la monnaie libre à travers mes recherches concernant les systèmes de fichiers. Travaillant principalement sur des infrastructures d'hébergement distribué, j'ai utilisé différents systèmes de réplication de fichiers comme glusterfs, cephfs, pour en arriver à ipfs. C'est en cherchant une alternative à filecoin, la crypto proposée par ipfs pour mettre en commun son espace de stockage, que je découvre la monnaie libre, on est en 2021.
|
||||
|
||||
Je rejoins Axiom-Team pour participer à la vulgarisation de la monnaie libre.
|
||||
|
||||
66
data/team/boris.md
Normal file
66
data/team/boris.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: "boris"
|
||||
fullName: "boris"
|
||||
role: "UX/UI Designer & Développeur Full Stack"
|
||||
availability: 40
|
||||
seniorityLevel: intermediate
|
||||
yearsExperience: 8
|
||||
joinDate: "2018-01"
|
||||
interests: ["UX/UI", "LLM", "Langues étrangères", "Médecine traditionnelle chinoise", "Feng Shui", "Tao", "Musique"]
|
||||
skills:
|
||||
- name: "UX"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "UI"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Figma"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "LLM"
|
||||
level: intermediate
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
- name: "JavaScript"
|
||||
level: intermediate
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "TypeScript"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "APIs"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Vis.js"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Polyvalence"
|
||||
- "Créativité"
|
||||
- "Curiosité"
|
||||
- "Multiculturalisme"
|
||||
projects:
|
||||
- "UX/UI de Ğecko (Figma)"
|
||||
- "App de médecine chinoise basée sur LLM"
|
||||
- "Site monnaie-libre.fr"
|
||||
- "Duniter | Accueil"
|
||||
- "cesium.app"
|
||||
- "Ğ1Quest (vue radar des annonces Ğchange)"
|
||||
- "Ğrocéliande (skin Ğchange style Amazon)"
|
||||
- "g1.business (routes commerciales)"
|
||||
- "Ğ1Gate (flux de monnaie en treemap)"
|
||||
- "H2G2 (guide du terraformeur terrien)"
|
||||
- "Ğ1 KDE Notifier"
|
||||
- "Simulateur RSA / Prime d'activité"
|
||||
- "Cerveau externe (Vis.js pour impros rap)"
|
||||
- "NoBS Troll-Emploi (moteur de recherche d'emploi)"
|
||||
---
|
||||
|
||||
Il est assez dispersé, "jack of all trade, master of none". Ces derniers temps, il passe beaucoup de temps à faire de la génération de musiques rigolotes (ou autre) avec les LLM et Suno. Il aime les langues étrangères (l'anglais surtout), la médecine traditionnelle chinoise, le Feng Shui (le tao en général). Il est communiste. Il a bossé sur l'UX/UI de Ğecko (via Figma). Grâce à Cursor, il développe une app de médecine chinoise basée sur les LLM. Dans la Ğ1, il a essayé de contribuer à l'onboarding (il a refait le site monnaie-libre.fr, Duniter | Accueil, et fait le site cesium.app). Il a aussi fait des clients Ğchange : Ğ1Quest (une projection des annonces Ğchange, notamment en "vue radar"), Ğrocéliande (un genre de skin pour Ğchange calqué sur l'interface d'Amazon, et qui ne prend que les annonces avec "envoi possible" dans la description), g1.business (qui permet de repérer les "routes commerciale", de faire correspondre pour un produit l'offre d'un endroit et la demande à un endroit distant, et qui projette sur une carte les moyens de productions disponibles à la location en Ğ1). Il a aussi fait Ğ1Gate (qui permet de suivre les flux de monnaie en vue "treemap"), H2G2 "le guide du terraformeur terrien" (une vue à la recette MineCraft de choses qu'on peut produire "dans la vraie vie"), Ğ1 KDE Notifier (Un petit outil pour être notifié de mouvements sur un portefeuille Ğ1), un Simulateur RSA / Prime d'activité (Un simulateur RSA/prime d'activité plus très à jour au niveau des données, mais qui permet de se rendre compte à quel point le fonctionnement de la prime d'activité est complètement stupide, et incite à éviter de travailler de façon trop importante trop ponctuellement, si on ne veut pas risquer de perdre de l'argent en allant se casser le cul au boulot), Cerveau externe (Un truc fait avec Vis.js, pour projeter des mots, colorés suivant la rime, regroupés autour des consonnes, et liés s'ils appartiennent à un même thème. Dans l'idée de faire des impros de rap avec. Proto sans réelle interface utilisateur utilisable par les moldus. Faire F5 pour raffraîchir et ainsi avoir un autre graphe de mots.), NoBS Troll-Emploi (Un moteur de recherche d'emploi basé sur l'API Pôle-Emploi et qui permet d'avoir plus de filtres : mots-clefs à exclure, pas de tutoiement, pas de "digital", etc… Idéal pour les gens qui, certes, acceptent d'être exploités lorsqu'ils développent du logiciel, mais veulent diminuer au maximum la quantité de bullshit dans leur job).
|
||||
|
||||
37
data/team/elois.md
Normal file
37
data/team/elois.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: "elois"
|
||||
fullName: "Eloïs"
|
||||
role: "Développeur Blockchain"
|
||||
availability: 25
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 5
|
||||
joinDate: "2019-01"
|
||||
interests: ["Blockchain", "Rust", "Migration", "Cryptographie"]
|
||||
skills:
|
||||
- name: "Rust"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "blockchain"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Substrate"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "migration"
|
||||
level: expert
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Autodidactie"
|
||||
- "Recherche"
|
||||
- "Architecture"
|
||||
projects:
|
||||
- "Rustification de Duniter v1"
|
||||
- "Duniter v2S"
|
||||
---
|
||||
|
||||
A appris les technologies blockchain en autodidact, travaillé sur la "rustification" (passage en Rust) de Duniter v1, puis bossé chez MoonPay.
|
||||
|
||||
47
data/team/fred.md
Normal file
47
data/team/fred.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: "fred"
|
||||
fullName: "Fred"
|
||||
role: "Développeur & Architecte Systèmes Décentralisés"
|
||||
availability: 40
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 20
|
||||
joinDate: "2014-01"
|
||||
interests: ["IPFS", "Secure ScuttleButt", "Nostr", "TiddlyWiki", "ThreeFold", "Systèmes décentralisés"]
|
||||
skills:
|
||||
- name: "IPFS"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "Secure ScuttleButt"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
- name: "Nostr"
|
||||
level: expert
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "TiddlyWiki"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "développement"
|
||||
level: expert
|
||||
years: 20
|
||||
lastUsed: "2024-12"
|
||||
- name: "ThreeFold"
|
||||
level: intermediate
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Architecture"
|
||||
- "Entrepreneuriat"
|
||||
- "Innovation"
|
||||
projects:
|
||||
- "Astroport (système d'information combinant Ğ1, IPFS et Nostr)"
|
||||
- "G1SMS (système de paiement par SMS en Ğ1)"
|
||||
- "G1billet (paper wallet pour la Ğ1)"
|
||||
- "Linkeo (entreprise)"
|
||||
---
|
||||
|
||||
A monté une boite (Linkeo) qui a bouffé une partie du marché de PagesJaunes début/milieu des années 2000. Très intéressé (et sachant) sur IPFS, Secure ScuttleButt, Nostr et TiddlyWiki. Il développe Astroport, un système d'information qui combine la Ğ1, IPFS et Nostr. Par le passé, il a aussi créé G1SMS (système de paiement par SMS en Ğ1) et G1billet (paper wallet pour la Ğ1).
|
||||
|
||||
34
data/team/hugo.md
Normal file
34
data/team/hugo.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: "hugo"
|
||||
fullName: "Hugo Trentesaux"
|
||||
role: "Financement & Gestion"
|
||||
availability: 20
|
||||
seniorityLevel: intermediate
|
||||
yearsExperience: 5
|
||||
joinDate: "2017-01"
|
||||
interests: ["Financement", "Gestion", "Rédaction", "Administration"]
|
||||
skills:
|
||||
- name: "financement"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "rédaction"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "gestion"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Rédaction"
|
||||
- "Administration"
|
||||
- "Gestion de projet"
|
||||
projects:
|
||||
- "Dossier de financement Ğecko (ADEME)"
|
||||
---
|
||||
|
||||
Je m'intéresse à la Ğ1 depuis 2017 et pense que l'association Axiom Team constitue une base juridique utile car nécessaire pour de nombreuses interactions avec le monde €.
|
||||
|
||||
J'ai travaillé sur le dossier de financement de Ǧecko auprès de l'ADEME avec succès. À l'avenir, je compte participer au fonctionnement d'Axiom Team, et à la partie rédactionnelle des dossiers de financement.
|
||||
|
||||
69
data/team/manuTopik.md
Normal file
69
data/team/manuTopik.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: "manuTopik"
|
||||
fullName: "ManUtopiK"
|
||||
role: "Développeur Web Full Stack"
|
||||
availability: 40
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 12
|
||||
joinDate: "2014-01"
|
||||
interests: ["Web", "Alternatives", "Monnaie libre", "Solarpunk", "Intelligence collective"]
|
||||
skills:
|
||||
- name: "VueJS"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "Nuxt.js"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-11"
|
||||
- name: "JavaScript"
|
||||
level: expert
|
||||
years: 12
|
||||
lastUsed: "2024-12"
|
||||
- name: "TypeScript"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "CMS"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "web"
|
||||
level: expert
|
||||
years: 12
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Communication"
|
||||
- "Vulgarisation"
|
||||
- "Créativité"
|
||||
projects:
|
||||
- "monnaie-libre.fr"
|
||||
- "carte.monnaie-libre.fr"
|
||||
- "Doc silkaj"
|
||||
- "WotWizard-UI"
|
||||
- "g1lib"
|
||||
- "Duniter UI (nuxt - abandonné)"
|
||||
- "Extension web g1Compagnon (en cours)"
|
||||
- "Interface web pour g1Billet (en cours)"
|
||||
---
|
||||
|
||||
Diplomé dans le domaine des énergies renouvelables, mon côté "web enthousiaste" m'a finalement amené à faire du développement web depuis + de 12 ans.
|
||||
|
||||
Passionné par tout ce qui est "alternatif" et qui rend libre, j'ai découvert le concept de la monnaie libre en 2014. L'économie actuelle est à mes yeux le principal facteur du bordel que l'on a mis sur cette planète depuis des générations. J'espère en un monde un peu plus libre, auto gouverné en intelligence collective, et avec du #solarpunk comme horizon. Profitons des crises pour tout changer !
|
||||
|
||||
À fond sur VueJS ; il a créé un CMS basé sur VueJS.
|
||||
|
||||
## Contributions
|
||||
|
||||
- Développement et rédaction du site monnaie-libre.fr (Dépôt du site, de l'api)
|
||||
- Développement de la carte.monnaie-libre.fr (Dépôt)
|
||||
- Doc silkaj
|
||||
- WotWizard-UI
|
||||
- g1lib
|
||||
- Duniter UI avec nuxt (Abandonné)
|
||||
|
||||
## En cours
|
||||
|
||||
- Extension web g1Compagnon
|
||||
- Interface web pour g1Billet
|
||||
|
||||
52
data/team/poka.md
Normal file
52
data/team/poka.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: "poka"
|
||||
fullName: "Poka"
|
||||
role: "Développeur Full Stack & Administrateur Système"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure", "Automatisation", "Blockchain"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Dart"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Python"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
- name: "bash"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "ProxMox"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "infrastructure"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
- "Pédagogie"
|
||||
- "Maintenance système"
|
||||
projects:
|
||||
- "Ğecko"
|
||||
- "Ğ1-stats"
|
||||
- "jaklis"
|
||||
- "py-g1-migrator"
|
||||
- "Infrastructure Axiom-Team"
|
||||
---
|
||||
|
||||
Je suis contributeur actif sur le projet Duniter depuis 2016 aux RML7 de Laval.
|
||||
|
||||
Je code Ğecko en Flutter/Dart. Je maintiens aussi l'infra Axiom-Team, soit 2 serveurs ProxMox.
|
||||
|
||||
J'ai aussi codé Ğ1-stats en bash. Et jaklis en python. J'ai aussi codé py-g1-migrator
|
||||
|
||||
38
data/team/syoul.md
Normal file
38
data/team/syoul.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: "syoul"
|
||||
fullName: "Syoul"
|
||||
role: "Etudiant IPSSI - Alternance Admin Infrastructure Securisee chez AJR"
|
||||
availability: 50
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 1
|
||||
joinDate: "2024-06"
|
||||
interests: ["Autohebergement", "Proxmox", "Docker", "Infrastructure", "Securite"]
|
||||
skills:
|
||||
- name: "Proxmox"
|
||||
level: beginner
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "Docker"
|
||||
level: beginner
|
||||
years: 1
|
||||
lastUsed: "2024-12"
|
||||
- name: "Linux"
|
||||
level: beginner
|
||||
years: 1
|
||||
lastUsed: "2024-12"
|
||||
- name: "autohebergement"
|
||||
level: beginner
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Apprentissage"
|
||||
- "Curiosite"
|
||||
- "Autonomie"
|
||||
projects:
|
||||
- "Autohebergement personnel (Proxmox + Docker)"
|
||||
- "Alternance AJR - Administration Infrastructure"
|
||||
---
|
||||
|
||||
Etudiant a l'IPSSI depuis 6 mois, en alternance Administrateur Infrastructure Securisee chez AJR.
|
||||
|
||||
Gere son infrastructure personnelle avec Proxmox et Docker pour l'autohebergement de services.
|
||||
37
data/team/tuxmain.md
Normal file
37
data/team/tuxmain.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: "tuxmain"
|
||||
fullName: "tuxmain"
|
||||
role: "Étudiant Math & Cryptographie"
|
||||
availability: 20
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 3
|
||||
joinDate: "2022-01"
|
||||
interests: ["Mathématiques", "Cryptographie", "Chiffrage", "Électronique", "Minetest"]
|
||||
skills:
|
||||
- name: "cryptographie"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "chiffrage"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "math"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "électronique"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Recherche"
|
||||
- "Analyse"
|
||||
- "Bidouille"
|
||||
projects:
|
||||
- "Administration serveur Minetest"
|
||||
- "Bidouille électronique"
|
||||
---
|
||||
|
||||
Étudiant en math. Bien compétent sur la cryptographie, le chiffrage, les conversions de clef d'une base en une autre. Administrateur de serveur Minetest. Il bidouille aussi de l'électronique.
|
||||
|
||||
28
data/team/vivien.md
Normal file
28
data/team/vivien.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: "vivien"
|
||||
fullName: "Vivien"
|
||||
role: "Développeur"
|
||||
availability: 40
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 2
|
||||
joinDate: "2023-01"
|
||||
interests: ["Cesium", "Godot", "Jeux", "Cartes Magic"]
|
||||
skills:
|
||||
- name: "Cesium"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
- name: "Godot"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Apprentissage"
|
||||
- "Curiosité"
|
||||
projects:
|
||||
- "Contribution à Cesium"
|
||||
- "Développement en Godot"
|
||||
---
|
||||
|
||||
Se forme pour contribuer à certains logiciels de la Ğ1 (Cesium). Développe aussi en Godot. Passionné de jeux (cartes Magic notamment).
|
||||
|
||||
54
data/team/yvv.md
Normal file
54
data/team/yvv.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: "yvv"
|
||||
fullName: "Yvv"
|
||||
role: "Gestion & Mobilisation"
|
||||
availability: 70
|
||||
seniorityLevel: senior
|
||||
yearsExperience: 10
|
||||
joinDate: "2015-01"
|
||||
interests: ["Gestion", "Mobilisation", "Économie du don", "Wiki", "Médiathèque"]
|
||||
skills:
|
||||
- name: "gestion"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
- name: "médiathèque"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
- name: "wiki"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Gestion"
|
||||
- "Organisation"
|
||||
- "Mobilisation"
|
||||
- "Communication"
|
||||
projects:
|
||||
- "Tuyauterie autogestion des dons (UNL)"
|
||||
- "WishBounty v2"
|
||||
- "FAQs version wiki"
|
||||
- "Médiathèque (nocodb)"
|
||||
- "Librodrome"
|
||||
- "Conserverie éphémère mobile"
|
||||
---
|
||||
|
||||
Vieux bouc dans le CA, je tire ma révérence en tant que secrétaire. Focus sur ce qui m'intéresse le plus, nouvelle forme de mobilisation.
|
||||
|
||||
## Pour mission UNL
|
||||
|
||||
- Aboutir la tuyauterie autogestion des dons.
|
||||
- L'élargir pour une v2 sur … un goût de paradis, le WishBounty.
|
||||
|
||||
## Pour mission fédération - services aux monnaie-libristes
|
||||
|
||||
- Bosser sur une FAQs version wiki, si un mediawiki ou autre voit le jour.
|
||||
- Bosser sur une médiathèque, si un nocodb ou autre voit le jour.
|
||||
|
||||
## Pour ML
|
||||
|
||||
- Diffuser mon bouquin "une économie du don - enfin concevable" et m'en servir de support pour mener des ateliers éco et "passer la seconde".
|
||||
- Lancer un événement structurant, le Librodrome.
|
||||
- Lancer une expérience de production collective monnaie-libriste, probablement une conserverie éphémère mobile.
|
||||
|
||||
@@ -94,6 +94,53 @@ Ce document liste les technologies et compétences identifiées dans l'écosyst
|
||||
- **api-axiom-team-fr** : API pour le site Axiom
|
||||
- **Compétences requises** : REST APIs, GraphQL, documentation d'API
|
||||
|
||||
## Technologies d'Authentification et d'Identité
|
||||
|
||||
### Authentification et Autorisation
|
||||
|
||||
#### Microsoft Entra (concurrents)
|
||||
- **Utilisation** : Solution d'identité et d'accès cloud de Microsoft
|
||||
- **Description** : Plateforme d'identité en tant que service (IDaaS) qui fournit l'authentification unique (SSO), la gestion des identités et l'accès conditionnel. Alternative aux solutions d'authentification traditionnelles.
|
||||
- **Compétences requises** : Gestion d'identité cloud, SSO, intégration d'identité, sécurité des accès
|
||||
|
||||
#### AUTHZ et AUTHN
|
||||
- **Utilisation** : Concepts fondamentaux de sécurité
|
||||
- **Description** :
|
||||
- **AUTHN (Authentication)** : Vérification de l'identité d'un utilisateur (qui êtes-vous ?)
|
||||
- **AUTHZ (Authorization)** : Vérification des permissions d'accès (que pouvez-vous faire ?)
|
||||
- **Compétences requises** : Principes de sécurité, gestion des identités, contrôle d'accès, modèles de permissions
|
||||
|
||||
#### Better Auth
|
||||
- **Utilisation** : Bibliothèque d'authentification moderne
|
||||
- **Description** : Solution d'authentification open-source offrant une API simple et flexible pour gérer l'authentification dans les applications web. Supporte OAuth, email/password, et autres méthodes d'authentification.
|
||||
- **Compétences requises** : Développement web, authentification, OAuth, sécurité des applications
|
||||
|
||||
### Identité Décentralisée
|
||||
|
||||
#### DID et UCAN
|
||||
- **Utilisation** : Identifiants décentralisés et système d'autorisation
|
||||
- **Description** :
|
||||
- **DID (Decentralized Identifiers)** : Identifiants uniques décentralisés qui permettent aux utilisateurs de contrôler leur identité sans dépendre d'une autorité centrale
|
||||
- **UCAN (User Controlled Authorization Networks)** : Système d'autorisation basé sur des capacités (capabilities) où les utilisateurs contrôlent leurs propres permissions
|
||||
- **Compétences requises** : Identité décentralisée, Web3, cryptographie, systèmes d'autorisation basés sur les capacités
|
||||
|
||||
#### VC (Verifiable Credentials)
|
||||
- **Utilisation** : Credentials vérifiables pour l'identité numérique
|
||||
- **Description** : Standard W3C pour les credentials numériques qui peuvent être vérifiés cryptographiquement. Permet de créer des identités numériques portables et vérifiables sans dépendre d'une autorité centrale.
|
||||
- **Compétences requises** : Standards W3C, identité numérique, cryptographie, vérification de credentials, blockchain (optionnel)
|
||||
|
||||
### Protocoles d'Authentification
|
||||
|
||||
#### OpenID Connect
|
||||
- **Utilisation** : Protocole d'authentification et d'autorisation
|
||||
- **Description** : Couche d'identité construite sur OAuth 2.0 qui permet aux clients de vérifier l'identité d'un utilisateur basée sur l'authentification effectuée par un serveur d'autorisation. Standard de l'industrie pour l'authentification fédérée.
|
||||
- **Compétences requises** : OAuth 2.0, protocoles d'authentification, intégration SSO, sécurité web
|
||||
|
||||
#### SPIFFE
|
||||
- **Utilisation** : Identité sécurisée pour les workloads en production
|
||||
- **Description** : SPIFFE (Secure Production Identity Framework For Everyone) fournit un cadre pour identifier et authentifier les workloads dans des environnements hétérogènes et distribués. Utilise des identités basées sur des certificats X.509 ou JWT.
|
||||
- **Compétences requises** : Sécurité des microservices, identité des workloads, mTLS, infrastructure distribuée, Kubernetes, service mesh
|
||||
|
||||
## Technologies d'Infrastructure Décentralisée
|
||||
|
||||
### ThreeFold
|
||||
@@ -1,19 +1,34 @@
|
||||
version: '3.8'
|
||||
# Convention de nommage : user-project-branch (ex: ajr-techradardev-main)
|
||||
# Permet plusieurs instances en parallele (prod/test) sans collision
|
||||
name: ${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}
|
||||
|
||||
services:
|
||||
radar-business:
|
||||
container_name: laplank-radar-business
|
||||
container_name: ${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}-app
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.business
|
||||
# Si vous utilisez une image pré-bâtie, décommentez image et commentez build
|
||||
# image: votre-registre/laplank-radar-business:latest
|
||||
pull: true
|
||||
args:
|
||||
BUILD_DATE: "${BUILD_DATE:-$(date +%s)}"
|
||||
BUILD_VERSION: "${BUILD_VERSION:-dev}"
|
||||
CACHE_BUST: "${CACHE_BUST:-$(date +%s%N)}"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3004:3000" # Mappe le port 3004 de l'hôte vers le port 3000 du conteneur
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
# Optionnel : Persistance des logs si nécessaire
|
||||
# volumes:
|
||||
# - ./logs:/app/logs
|
||||
labels:
|
||||
# Registrator lit l'IP du conteneur depuis le reseau "sonic" (-useIpFromNetwork sonic)
|
||||
# et enregistre le service dans Consul avec le tag urlprefix- -> Fabio route vers ce service
|
||||
- SERVICE_3000_NAME=${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}-app-3000
|
||||
- SERVICE_3000_TAGS=urlprefix-${RADAR_DOMAIN}/*
|
||||
- SERVICE_3000_CHECK_TCP=true
|
||||
# sonic-acme-1 (acme-companion) emet le cert TLS et le copie dans /host/certs/
|
||||
# Fabio le detecte automatiquement par SNI pour HTTPS
|
||||
- LETSENCRYPT_HOST=${RADAR_DOMAIN}
|
||||
networks:
|
||||
- sonic
|
||||
|
||||
networks:
|
||||
sonic:
|
||||
# Reseau externe existant sur le serveur (partage avec Registrator/Consul/Fabio)
|
||||
external: true
|
||||
|
||||
195
docker/add_team_link.py
Normal file
195
docker/add_team_link.py
Normal file
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
||||
f = "radar-app/src/components/Navigation/Navigation.tsx"
|
||||
|
||||
try:
|
||||
# Vérifier que le fichier existe
|
||||
if not os.path.exists(f):
|
||||
print(f"❌ Fichier {f} introuvable", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(f, 'r', encoding='utf-8') as file:
|
||||
content = file.read()
|
||||
|
||||
# VÉRIFICATION PRÉLIMINAIRE: Détecter les doublons structurels
|
||||
print("🔍 Vérification de la structure du composant...")
|
||||
|
||||
# Compter les éléments structurels
|
||||
nav_patterns = [
|
||||
r'<nav\s',
|
||||
r'className.*[Nn]avigation',
|
||||
r'export\s+(default\s+)?function\s+Navigation',
|
||||
r'const\s+Navigation\s*='
|
||||
]
|
||||
nav_count = sum(len(re.findall(pattern, content)) for pattern in nav_patterns)
|
||||
|
||||
# Compter les <ul> (peut y en avoir 2 pour mobile/desktop)
|
||||
ul_count = len(re.findall(r'<ul[^>]*>', content))
|
||||
|
||||
# Compter les logos (Image avec logo ou logoFile)
|
||||
logo_patterns = [
|
||||
r'logoFile',
|
||||
r'logo\.svg',
|
||||
r'[Ll]ogo',
|
||||
r'Image.*logo'
|
||||
]
|
||||
logo_count = sum(len(re.findall(pattern, content)) for pattern in logo_patterns)
|
||||
|
||||
# Compter les fonctions Navigation
|
||||
function_count = len(re.findall(r'(export\s+(default\s+)?function\s+Navigation|const\s+Navigation\s*=\s*\(|function\s+Navigation\s*\()', content))
|
||||
|
||||
print(f"📊 Structure détectée: {nav_count} nav, {ul_count} ul, {logo_count} logo, {function_count} fonction(s)")
|
||||
|
||||
# Détecter les duplications structurelles
|
||||
if function_count > 1:
|
||||
print(f"⚠️ ATTENTION: {function_count} fonction(s) Navigation détectée(s) - possible duplication du composant", file=sys.stderr)
|
||||
# Extraire uniquement la première fonction Navigation
|
||||
matches = list(re.finditer(r'(export\s+(default\s+)?function\s+Navigation|const\s+Navigation\s*=\s*\(|function\s+Navigation\s*\()', content))
|
||||
if len(matches) > 1:
|
||||
# Garder seulement jusqu'à la fin de la première fonction
|
||||
first_end = matches[1].start() if len(matches) > 1 else len(content)
|
||||
content = content[:first_end]
|
||||
# Trouver la fin de la fonction (dernière accolade fermante avant la prochaine fonction)
|
||||
brace_count = 0
|
||||
in_function = False
|
||||
for i, char in enumerate(content[matches[0].start():], matches[0].start()):
|
||||
if char == '{':
|
||||
brace_count += 1
|
||||
in_function = True
|
||||
elif char == '}':
|
||||
brace_count -= 1
|
||||
if in_function and brace_count == 0:
|
||||
content = content[:i+1]
|
||||
break
|
||||
print(f"✅ Duplication du composant supprimée")
|
||||
|
||||
if ul_count > 3: # Plus de 3 ul suggère une duplication
|
||||
print(f"⚠️ ATTENTION: {ul_count} éléments <ul> détectés - possible duplication", file=sys.stderr)
|
||||
|
||||
if logo_count > 5: # Plus de 5 références au logo suggère une duplication
|
||||
print(f"⚠️ ATTENTION: {logo_count} références au logo détectées - possible duplication", file=sys.stderr)
|
||||
|
||||
# ÉTAPE 1: Supprimer TOUS les liens Équipe existants (même s'il n'y en a qu'un)
|
||||
print("🧹 Nettoyage de tous les liens Équipe existants...")
|
||||
|
||||
# APPROCHE AGRESSIVE: Supprimer tous les blocs <li> contenant un lien vers /team
|
||||
# Utiliser plusieurs patterns pour capturer tous les cas possibles
|
||||
# Pattern 1: <li>...<Link href="/team"...>...</Link>...</li>
|
||||
team_link_block_pattern = r'<li[^>]*>.*?<Link[^>]*href=["\']/?team(/|\.html)?["\'][^>]*>.*?</Link>.*?</li>'
|
||||
content_cleaned = re.sub(team_link_block_pattern, '', content, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
# Pattern 2: Supprimer aussi les lignes contenant href="/team" même si elles ne sont pas dans un <li> complet
|
||||
content_cleaned = re.sub(r'.*href=["\']/?team(/|\.html)?["\'].*\n', '', content_cleaned, flags=re.IGNORECASE)
|
||||
|
||||
# Pattern 3: Supprimer les blocs <li> qui pourraient contenir /team sur plusieurs lignes (format différent)
|
||||
content_cleaned = re.sub(r'<li[^>]*>.*?/team.*?</li>', '', content_cleaned, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
# Compter combien de liens ont été supprimés
|
||||
remaining_before = len(re.findall(r'href=["\']/?team(/|\.html)?["\']', content))
|
||||
remaining_after = len(re.findall(r'href=["\']/?team(/|\.html)?["\']', content_cleaned))
|
||||
team_links_removed = remaining_before - remaining_after
|
||||
|
||||
if team_links_removed > 0:
|
||||
print(f"✅ {team_links_removed} lien(s) Équipe supprimé(s) (regex multiligne)")
|
||||
elif remaining_after > 0:
|
||||
print(f"⚠️ {remaining_after} lien(s) Équipe encore présent(s) après nettoyage regex, nettoyage manuel...")
|
||||
# Nettoyage manuel ligne par ligne si la regex n'a pas tout capturé
|
||||
lines = content_cleaned.splitlines(keepends=True)
|
||||
if lines and not lines[-1].endswith('\n'):
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
|
||||
new_lines = []
|
||||
skip_team_link = False
|
||||
manual_removed = 0
|
||||
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
|
||||
# Détecter le début d'un lien Équipe
|
||||
team_link_match = re.search(r'href=["\']/?team(/|\.html)?["\']|href=\{["\']/?team', line)
|
||||
if team_link_match and not skip_team_link:
|
||||
skip_team_link = True
|
||||
manual_removed += 1
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if skip_team_link:
|
||||
if '</li>' in line:
|
||||
skip_team_link = False
|
||||
i += 1
|
||||
continue
|
||||
|
||||
new_lines.append(line)
|
||||
i += 1
|
||||
|
||||
if manual_removed > 0:
|
||||
content_cleaned = ''.join(new_lines)
|
||||
print(f"✅ {manual_removed} lien(s) Équipe supplémentaire(s) supprimé(s) (nettoyage manuel)")
|
||||
|
||||
lines = content_cleaned.splitlines(keepends=True)
|
||||
if lines and not lines[-1].endswith('\n'):
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
|
||||
# ÉTAPE 2: Vérifier qu'il n'y a plus aucun lien team avant d'ajouter
|
||||
final_check = len(re.findall(r'href=["\']/?team(/|\.html)?["\']', content_cleaned))
|
||||
if final_check > 0:
|
||||
print(f"⚠️ ATTENTION: {final_check} lien(s) Équipe encore présent(s) après nettoyage, nettoyage supplémentaire...", file=sys.stderr)
|
||||
# Nettoyage supplémentaire avec une regex plus agressive
|
||||
content_cleaned = re.sub(r'.*?href=["\']/?team(/|\.html)?["\'].*?\n', '', content_cleaned, flags=re.MULTILINE | re.IGNORECASE)
|
||||
# Supprimer aussi les blocs <li> vides qui pourraient rester
|
||||
content_cleaned = re.sub(r'<li[^>]*>\s*</li>\s*\n', '', content_cleaned)
|
||||
final_check_2 = len(re.findall(r'href=["\']/?team(/|\.html)?["\']', content_cleaned))
|
||||
if final_check_2 > 0:
|
||||
print(f"❌ ERREUR: {final_check_2} lien(s) Équipe toujours présent(s) après nettoyage supplémentaire!", file=sys.stderr)
|
||||
print("📄 Contenu autour des liens restants:", file=sys.stderr)
|
||||
for match in re.finditer(r'href=["\']/?team(/|\.html)?["\']', content_cleaned):
|
||||
start = max(0, match.start() - 50)
|
||||
end = min(len(content_cleaned), match.end() + 50)
|
||||
print(f" {content_cleaned[start:end]}", file=sys.stderr)
|
||||
else:
|
||||
print(f"✅ Tous les liens Équipe supprimés après nettoyage supplémentaire")
|
||||
|
||||
# ÉTAPE 3: Ajouter un seul lien Équipe au bon endroit
|
||||
insert_idx = -1
|
||||
for i, line in enumerate(lines):
|
||||
if 'href="/overview"' in line:
|
||||
for j in range(i, min(i+10, len(lines))):
|
||||
if '</Link>' in lines[j] and j+1 < len(lines) and '</li>' in lines[j+1]:
|
||||
insert_idx = j + 2
|
||||
break
|
||||
break
|
||||
|
||||
if insert_idx > 0:
|
||||
new_lines = lines[:insert_idx] + [
|
||||
' <li className={styles.item}>\n',
|
||||
' <Link href="/team">\n',
|
||||
' <span className={styles.label}>👥 Équipe</span>\n',
|
||||
' </Link>\n',
|
||||
' </li>\n'
|
||||
] + lines[insert_idx:]
|
||||
with open(f, 'w', encoding='utf-8') as file:
|
||||
file.writelines(new_lines)
|
||||
|
||||
# Vérifier qu'il n'y a qu'un seul lien maintenant (inclut /team, /team/, /team.html)
|
||||
with open(f, 'r', encoding='utf-8') as file:
|
||||
final_content = file.read()
|
||||
final_count = len(re.findall(r'href=["\']/?team(/|\.html)?["\']', final_content))
|
||||
|
||||
if final_count == 1:
|
||||
print("✅ Navigation.tsx modifié - 1 seul lien Équipe présent")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"⚠️ Attention: {final_count} lien(s) Équipe détecté(s) après modification", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("❌ Impossible de trouver l'emplacement pour insérer le lien", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur Python: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
97
docker/add_team_link.sh
Normal file
97
docker/add_team_link.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "🔧 Modification de Navigation.tsx pour le lien Équipe..."
|
||||
|
||||
NAV_FILE="radar-app/src/components/Navigation/Navigation.tsx"
|
||||
|
||||
# Vérifier que le fichier existe
|
||||
if [ ! -f "$NAV_FILE" ]; then
|
||||
echo "❌ Fichier $NAV_FILE introuvable"
|
||||
echo "📁 Répertoire actuel: $(pwd)"
|
||||
echo "📁 Contenu de radar-app/src/components/:"
|
||||
ls -la radar-app/src/components/ 2>/dev/null || echo "Répertoire non trouvé"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Exécuter le script Python
|
||||
if python3 /tmp/add_team_link.py; then
|
||||
# Vérifier le résultat (inclut /team, /team/, /team.html)
|
||||
team_count=$(grep -cE 'href="/team|href=\{"/team|href=["'"'"']/team' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
echo "📊 Nombre d'occurrences trouvées: $team_count"
|
||||
|
||||
if [ "$team_count" -eq "1" ]; then
|
||||
echo "✅ Lien Équipe présent (1 occurrence)"
|
||||
elif [ "$team_count" -gt "1" ]; then
|
||||
echo "❌ ERREUR: $team_count occurrences détectées - affichage des occurrences:"
|
||||
grep -nE 'href="/team|href=\{"/team|href=["'"'"']/team' "$NAV_FILE" || true
|
||||
echo "⚠️ Relance du nettoyage..."
|
||||
python3 /tmp/add_team_link.py
|
||||
final_count=$(grep -cE 'href="/team|href=\{"/team|href=["'"'"']/team' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$final_count" -gt "1" ]; then
|
||||
echo "❌ ERREUR CRITIQUE: $final_count occurrences encore présentes après nettoyage!"
|
||||
echo "📄 Aperçu complet de Navigation.tsx:"
|
||||
cat "$NAV_FILE" || true
|
||||
exit 1
|
||||
else
|
||||
echo "✅ Après nettoyage: $final_count occurrence(s)"
|
||||
fi
|
||||
else
|
||||
echo "❌ Lien Équipe non trouvé après modification"
|
||||
echo "📄 Aperçu de Navigation.tsx (premières 50 lignes):"
|
||||
head -50 "$NAV_FILE" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# VÉRIFICATIONS POST-MODIFICATION: Détecter les doublons structurels
|
||||
echo "🔍 Vérification des doublons structurels..."
|
||||
|
||||
# Compter les fonctions Navigation
|
||||
function_count=$(grep -cE '(export\s+(default\s+)?function\s+Navigation|const\s+Navigation\s*=\s*\(|function\s+Navigation\s*\()' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$function_count" -gt "1" ]; then
|
||||
echo "❌ ERREUR: $function_count fonction(s) Navigation détectée(s) - duplication du composant!"
|
||||
echo "📄 Recherche des fonctions Navigation:"
|
||||
grep -nE '(export\s+(default\s+)?function\s+Navigation|const\s+Navigation\s*=\s*\(|function\s+Navigation\s*\()' "$NAV_FILE" || true
|
||||
exit 1
|
||||
else
|
||||
echo "✅ Composant Navigation unique ($function_count fonction)"
|
||||
fi
|
||||
|
||||
# Compter les éléments <nav> ou className Navigation
|
||||
nav_count=$(grep -cE '<nav\s|className.*[Nn]avigation' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$nav_count" -gt "2" ]; then
|
||||
echo "⚠️ ATTENTION: $nav_count éléments nav détectés (attendu: 1-2)"
|
||||
else
|
||||
echo "✅ Structure nav correcte ($nav_count élément(s))"
|
||||
fi
|
||||
|
||||
# Compter les éléments <ul>
|
||||
ul_count=$(grep -c '<ul' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$ul_count" -gt "3" ]; then
|
||||
echo "⚠️ ATTENTION: $ul_count éléments <ul> détectés (attendu: 1-3 pour mobile/desktop)"
|
||||
else
|
||||
echo "✅ Structure ul correcte ($ul_count élément(s))"
|
||||
fi
|
||||
|
||||
# Compter les références au logo
|
||||
logo_count=$(grep -cE 'logoFile|logo\.svg|[Ll]ogo' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$logo_count" -gt "5" ]; then
|
||||
echo "⚠️ ATTENTION: $logo_count références au logo détectées (possible duplication)"
|
||||
else
|
||||
echo "✅ Références logo correctes ($logo_count référence(s))"
|
||||
fi
|
||||
|
||||
# Vérifier qu'il n'y a qu'un seul export default
|
||||
export_count=$(grep -c 'export default' "$NAV_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$export_count" -gt "1" ]; then
|
||||
echo "❌ ERREUR: $export_count export default détectés - duplication du composant!"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ Export unique ($export_count export default)"
|
||||
fi
|
||||
|
||||
echo "✅ Toutes les vérifications structurelles passées"
|
||||
else
|
||||
echo "❌ Erreur lors de l'exécution du script Python"
|
||||
exit 1
|
||||
fi
|
||||
42
docker/patch_document.py
Normal file
42
docker/patch_document.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
# Script pour modifier _document.tsx et charger team-block-script.js en premier
|
||||
|
||||
import sys
|
||||
|
||||
doc_path = "radar-app/src/pages/_document.tsx"
|
||||
|
||||
try:
|
||||
with open(doc_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Ajouter l'import de Script si pas present
|
||||
if "import Script from 'next/script'" not in content and 'import Script from "next/script"' not in content:
|
||||
content = content.replace(
|
||||
'import { Head, Html, Main, NextScript } from "next/document";',
|
||||
'import { Head, Html, Main, NextScript } from "next/document";\nimport Script from "next/script";'
|
||||
)
|
||||
|
||||
# Ajouter le script dans <Head> avec strategy="beforeInteractive"
|
||||
if "team-block-script.js" not in content:
|
||||
# Trouver la fin de <Head /> et la remplacer par un <Head> avec contenu
|
||||
if "<Head />" in content:
|
||||
content = content.replace(
|
||||
"<Head />",
|
||||
'<Head>\n <Script src="/team-block-script.js" strategy="beforeInteractive" />\n </Head>'
|
||||
)
|
||||
elif "<Head>" in content and "</Head>" in content:
|
||||
# Ajouter avant </Head>
|
||||
content = content.replace(
|
||||
"</Head>",
|
||||
' <Script src="/team-block-script.js" strategy="beforeInteractive" />\n </Head>'
|
||||
)
|
||||
|
||||
with open(doc_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print("_document.tsx modifie pour charger team-block-script.js en premier")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"Erreur: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
5
docker/team-page.tsx
Normal file
5
docker/team-page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
// @ts-nocheck
|
||||
// Page vide - le contenu est injecté par team-block-script.js via iframe
|
||||
export default function TeamPage() {
|
||||
return null;
|
||||
}
|
||||
@@ -2,34 +2,44 @@
|
||||
|
||||
Bienvenue dans la documentation du projet AJR Technology Radar (CoeurBox).
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le Technology Radar AJR est une application web interactive qui présente les technologies, outils, méthodes et plateformes utilisées et évaluées par AJR. Il est basé sur le framework [aoe_technology_radar](https://github.com/AOEpeople/aoe_technology_radar).
|
||||
|
||||
Le radar est organisé en quatre quadrants et quatre anneaux (rings) pour classifier chaque technologie selon son niveau d'adoption et sa catégorie.
|
||||
|
||||
## Structure de la documentation
|
||||
|
||||
Cette documentation est organisée en plusieurs sections :
|
||||
La documentation est organisée en deux catégories principales :
|
||||
|
||||
- **[Architecture](./architecture.md)** - Structure du projet, organisation des fichiers et composants
|
||||
- **[Configuration](./configuration.md)** - Configuration du radar, quadrants, anneaux et personnalisation
|
||||
- **[Développement](./developpement.md)** - Guide pour développer et tester localement
|
||||
- **[Déploiement](./deploiement.md)** - Instructions pour déployer le radar en production
|
||||
- **[Contribution](./contribution.md)** - Guide pour ajouter de nouvelles technologies au radar
|
||||
### 📚 Documentation de l'application (`app/`)
|
||||
|
||||
### Radar Business
|
||||
Documentation technique sur l'utilisation, le développement et le déploiement de l'application :
|
||||
|
||||
- **[Guide Radar Business](./guide-radar-business.md)** - Guide d'utilisation du radar stratégique business
|
||||
- **[Analyse Stratégique](./analyse-strategique.md)** - Rapport d'analyse des patterns et recommandations
|
||||
- **[Stratégie d'Évolution Technique](./strategie-evolution-technique.md)** - Vision et roadmap technique
|
||||
- **[Technologies Duniter](./technologies-duniter.md)** - Liste des technologies de l'écosystème Duniter/Ğ1
|
||||
- **[Architecture](./app/architecture.md)** - Structure du projet, organisation des fichiers et composants
|
||||
- **[Configuration](./app/configuration.md)** - Configuration du radar, quadrants, anneaux et personnalisation
|
||||
- **[Développement](./app/developpement.md)** - Guide pour développer et tester localement
|
||||
- **[Déploiement](./app/deploiement.md)** - Instructions pour déployer le radar en production
|
||||
- **[Contribution](./app/contribution.md)** - Guide pour ajouter de nouvelles technologies au radar
|
||||
- **[Guide Radar Technologique Laplank](./app/guide-radar-business.md)** - Guide d'utilisation du radar technologique Laplank
|
||||
|
||||
### 📊 Données utilisées par l'application (`data/`)
|
||||
|
||||
Données métier et contenu utilisé par l'application pour générer le radar :
|
||||
|
||||
- **[Technologies Duniter](./data/technologies-duniter.md)** - Liste des technologies de l'écosystème Duniter/Ğ1
|
||||
- **[Profil Team](./data/profil-team.md)** - Profils et compétences des membres de l'équipe
|
||||
- **[Stratégie d'Évolution Technique](./data/strategie-evolution-technique.md)** - Vision et roadmap technique
|
||||
- **[Stratégie Business](./data/strategie-business.md)** - Analyse stratégique business
|
||||
- **[Opportunités DataViz](./data/opportunites-dataviz.md)** - Opportunités en dataviz
|
||||
- **[Opportunités DataViz Détails](./data/opportunites-dataviz-details.md)** - Détails des opportunités dataviz
|
||||
- **[Analyse Stratégique](./data/analyse-strategique.md)** - Rapport d'analyse généré automatiquement
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le Technology Radar AJR est une application web interactive qui présente les technologies, outils, méthodes et plateformes utilisées et évaluées par AJR. Il est basé sur le framework [aoe_technology_radar](https://github.com/AOEpeople/aoe_technology_radar), dont le code source est vendu dans le répertoire `radar-app/`.
|
||||
|
||||
Le radar est organisé en quatre quadrants et quatre anneaux (rings) pour classifier chaque technologie selon son niveau d'adoption et sa catégorie.
|
||||
|
||||
## Liens utiles
|
||||
|
||||
- **Radar en ligne** : https://www.coeurbox.syoul.fr
|
||||
- **Dépôt Git** : https://git.open.us.org/AJR/TechradarDev
|
||||
- **Framework source** : https://github.com/AOEpeople/aoe_technology_radar
|
||||
- **Framework source** : https://github.com/AOEpeople/aoe_technology_radar (code vendu dans `radar-app/`)
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
@@ -44,18 +54,18 @@ npm run serve
|
||||
|
||||
Puis ouvrir http://localhost:3000/techradar dans votre navigateur.
|
||||
|
||||
### Radar Business
|
||||
### Radar Technologique Laplank
|
||||
|
||||
Pour démarrer le radar business en local :
|
||||
Pour démarrer le radar technologique Laplank en local :
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run serve-business
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3004
|
||||
Le serveur démarre sur http://localhost:3006
|
||||
|
||||
**Note** : Le radar business est protégé par un mot de passe (`laplank-radar`).
|
||||
**Note** : Le radar technologique Laplank est protégé par un mot de passe (`laplank-radar`).
|
||||
|
||||
Pour plus de détails, consultez le [guide de développement](./developpement.md) et le [guide du radar business](./guide-radar-business.md).
|
||||
Pour plus de détails, consultez le [guide de développement](./app/developpement.md) et le [guide du radar technologique Laplank](./app/guide-radar-business.md).
|
||||
|
||||
|
||||
156
docs/app/README.md
Normal file
156
docs/app/README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Documentation AJR Technology Radar
|
||||
|
||||
Bienvenue dans la documentation du projet AJR Technology Radar (CoeurBox).
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le Technology Radar AJR est une application web interactive qui présente les technologies, outils, méthodes et plateformes utilisées et évaluées par AJR. Il est basé sur le framework [aoe_technology_radar](https://github.com/AOEpeople/aoe_technology_radar), dont le code source est vendu dans le répertoire `radar-app/`.
|
||||
|
||||
Le radar est organisé en quatre quadrants et quatre anneaux (rings) pour classifier chaque technologie selon son niveau d'adoption et sa catégorie.
|
||||
|
||||
## Structure de la documentation
|
||||
|
||||
Cette documentation est organisée en plusieurs sections :
|
||||
|
||||
### Documentation technique
|
||||
|
||||
- **[Architecture](./architecture.md)** - Structure du projet, organisation des fichiers et composants
|
||||
- **[Configuration](./configuration.md)** - Configuration du radar, quadrants, anneaux et personnalisation
|
||||
- **[Développement](./developpement.md)** - Guide pour développer et tester localement
|
||||
- **[Déploiement](./deploiement.md)** - Instructions pour déployer le radar en production
|
||||
- **[Contribution](./contribution.md)** - Guide pour ajouter de nouvelles technologies au radar
|
||||
- **[Guide Radar Business](./guide-radar-business.md)** - Guide d'utilisation du radar technologique Laplank
|
||||
- **[Page Équipe & Technologies](./guide-page-equipe.md)** - Documentation de la page de visualisation équipe/technologies
|
||||
- **[Dépannage](./troubleshooting.md)** - Guide de résolution des problèmes courants
|
||||
|
||||
### Données du Radar Technologique Laplank
|
||||
|
||||
Les données utilisées par l'application sont dans le dossier [`../data/`](../data/) :
|
||||
|
||||
- **[Technologies Duniter](../data/technologies-duniter.md)** - Liste des technologies de l'écosystème Duniter/Ğ1
|
||||
- **[Profils Team](../data/team/)** - Profils individuels des membres de l'équipe (fichiers Markdown)
|
||||
- **[Stratégie d'Évolution Technique](../data/strategie-evolution-technique.md)** - Vision et roadmap technique
|
||||
- **[Stratégie Business](../data/strategie-business.md)** - Analyse stratégique business
|
||||
- **[Analyse Stratégique](../data/analyse-strategique.md)** - Rapport d'analyse généré automatiquement
|
||||
|
||||
## Liens utiles
|
||||
|
||||
- **Radar en ligne** : https://www.coeurbox.syoul.fr
|
||||
- **Radar Technologique Laplank** : http://laplank.techradar.syoul.fr:3006
|
||||
- **Dépôt Git** : https://git.open.us.org/AJR/TechradarDev
|
||||
- **Framework source** : https://github.com/AOEpeople/aoe_technology_radar (code vendu dans `radar-app/`)
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
### Radar Principal
|
||||
|
||||
Pour démarrer rapidement le radar principal en local :
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Puis ouvrir http://localhost:3000/techradar dans votre navigateur.
|
||||
|
||||
### Radar Technologique Laplank
|
||||
|
||||
Pour démarrer le radar technologique Laplank en local :
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run serve-business
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3006
|
||||
|
||||
**Note** : Le radar technologique Laplank est protégé par un mot de passe (`laplank-radar`).
|
||||
|
||||
Pour plus de détails, consultez le [guide de développement](./developpement.md) et le [guide du radar technologique Laplank](./guide-radar-business.md).
|
||||
|
||||
## Fonctionnalités principales
|
||||
|
||||
### Radar Technologique
|
||||
|
||||
- **Visualisation interactive** : Graphique radar avec zoom et filtres
|
||||
- **Historique par release** : Suivi de l'évolution des technologies au fil du temps
|
||||
- **Quadrants business** : Classification selon l'impact business
|
||||
- **Anneaux classiques** : Hold, Assess, Trial, Adopt
|
||||
- **Filtrage par tags** : Recherche et filtrage des technologies
|
||||
- **Pages de stratégie** : Accès à la stratégie technique, business et opportunités
|
||||
|
||||
### Page Équipe & Technologies
|
||||
|
||||
- **Graphe réseau** : Visualisation des connexions technologies/membres
|
||||
- **Matrice de congestion** : Identification des technologies avec faible couverture
|
||||
- **Équipe de genèse MVP** : Composition automatique d'une équipe minimale
|
||||
|
||||
## Technologies utilisées
|
||||
|
||||
- **Next.js** : Framework React pour la génération statique
|
||||
- **React** : Bibliothèque JavaScript pour l'interface utilisateur
|
||||
- **TypeScript** : Typage statique
|
||||
- **Cytoscape.js** : Visualisation de graphes
|
||||
- **ECharts** : Visualisation de données (heatmaps, scatter plots)
|
||||
- **Markdown** : Format des blips et profils
|
||||
- **YAML** : Métadonnées dans les fichiers Markdown
|
||||
|
||||
## Structure du projet
|
||||
|
||||
```
|
||||
TechradarDev/
|
||||
├── radar-business/ # Contenu du radar business (actif)
|
||||
│ ├── 2025-01-15/ # Blips organisés par release
|
||||
│ └── config-business.json # Configuration
|
||||
├── docs/
|
||||
│ ├── app/ # Documentation technique
|
||||
│ └── data/ # Données métier
|
||||
│ └── team/ # Profils équipe individuels
|
||||
├── public/ # Fichiers statiques
|
||||
│ ├── team-block-script.js # Script principal pour la page equipe
|
||||
│ └── team-visualization-data.json # Donnees equipe
|
||||
├── scripts/ # Scripts utilitaires
|
||||
└── Dockerfile.business # Dockerfile pour le déploiement
|
||||
```
|
||||
|
||||
## Commandes principales
|
||||
|
||||
```bash
|
||||
# Développement
|
||||
npm run serve-business # Démarrer le serveur de développement (port 3006)
|
||||
|
||||
# Génération de données
|
||||
node scripts/generate-team-visualization-data.js # Générer les données équipe
|
||||
node scripts/extract-technologies.js # Extraire les technologies
|
||||
node scripts/analyze-business-metrics.js # Analyser les métriques
|
||||
|
||||
# Build
|
||||
npm run build # Build de production
|
||||
```
|
||||
|
||||
## Problèmes courants
|
||||
|
||||
Consultez le [guide de dépannage](./troubleshooting.md) pour les problèmes courants :
|
||||
|
||||
- Doublons de liens dans la navigation
|
||||
- Seulement quelques blips affichés
|
||||
- Page équipe inaccessible
|
||||
- Données de visualisation manquantes
|
||||
|
||||
## Contribution
|
||||
|
||||
Pour contribuer au projet :
|
||||
|
||||
1. Lire le [guide de contribution](./contribution.md)
|
||||
2. Créer une branche pour vos modifications
|
||||
3. Ajouter/modifier les blips dans `radar-business/2025-01-15/`
|
||||
4. Tester localement avec `npm run serve-business`
|
||||
5. Créer une pull request
|
||||
|
||||
## Support
|
||||
|
||||
Pour toute question :
|
||||
- Consulter la documentation dans `docs/app/`
|
||||
- Vérifier le [guide de dépannage](./troubleshooting.md)
|
||||
- Ouvrir une issue sur le dépôt Git
|
||||
- Contacter l'équipe technique
|
||||
260
docs/app/architecture.md
Normal file
260
docs/app/architecture.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# Architecture du projet
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le projet AJR Technology Radar est une application web statique construite avec le framework `aoe_technology_radar` (basé sur Next.js), dont le code source est vendu dans le répertoire `radar-app/`. L'application génère un site web interactif à partir de fichiers Markdown organisés par dates de release.
|
||||
|
||||
## Structure des répertoires
|
||||
|
||||
```
|
||||
TechradarDev/
|
||||
├── radar/ # Contenu du radar principal organisé par dates (déprécié)
|
||||
│ └── 2025-01-15/ # Release de janvier 2025
|
||||
├── radar-business/ # Contenu du radar business (actif)
|
||||
│ ├── 2025-01-15/ # Release business de janvier 2025
|
||||
│ ├── config-business.json # Configuration du radar business
|
||||
│ ├── FORMAT-BLIP.md # Format des blips business
|
||||
│ └── README.md # Documentation du radar business
|
||||
├── public/ # Fichiers statiques publics
|
||||
│ ├── images/ # Images utilisees dans les descriptions
|
||||
│ ├── logo.svg # Logo AJR
|
||||
│ ├── favicon.ico # Icone du site
|
||||
│ ├── robots.txt # Configuration pour les robots
|
||||
│ ├── strategie-script.js # Script pour les pages de strategie dynamiques
|
||||
│ ├── strategie-link.js # Script alternatif pour les liens strategie
|
||||
│ ├── team-block-script.js # Script principal pour la page equipe (injection DOM)
|
||||
│ ├── _team-content # Contenu HTML de reference pour la page equipe
|
||||
│ └── team-visualization-data.json # Donnees JSON pour les visualisations
|
||||
├── scripts/ # Scripts utilitaires
|
||||
│ ├── serve-business.sh # Script pour servir le radar business en local
|
||||
│ ├── start-business.sh # Script de démarrage pour Docker
|
||||
│ ├── extract-technologies.js # Extraction des technologies depuis la doc
|
||||
│ ├── analyze-business-metrics.js # Analyse des métriques business
|
||||
│ ├── generate-team-visualization-data.js # Génération des données équipe
|
||||
│ ├── create-team-page.sh # Script pour créer la page équipe
|
||||
│ └── patch-navigation.sh # Script pour patcher Navigation.tsx
|
||||
├── docker/ # Configuration Docker pour le déploiement
|
||||
│ ├── Dockerfile # Image Docker de production
|
||||
│ ├── docker-compose.yml # Configuration Docker Compose
|
||||
│ ├── docker-compose.labels.yml # Labels pour le déploiement
|
||||
│ └── docker-compose.local.yml # Configuration locale
|
||||
├── docs/ # Documentation du projet
|
||||
│ ├── app/ # Documentation technique de l'application
|
||||
│ └── data/ # Données métier et contenu
|
||||
│ └── team/ # Profils individuels des membres de l'équipe
|
||||
├── radar-app/ # Framework aoe_technology_radar (code vendu dans le repo)
|
||||
│ ├── src/ # Code source Next.js du framework
|
||||
│ │ ├── pages/ # Pages Next.js (routes)
|
||||
│ │ │ └── team.tsx # Page /team générée par Dockerfile
|
||||
│ │ └── components/ # Composants React
|
||||
│ │ └── Navigation/ # Composant de navigation
|
||||
│ │ └── Navigation.tsx # Modifié par Dockerfile pour ajouter le lien Équipe
|
||||
│ ├── data/ # Données du radar
|
||||
│ │ ├── config.json # Configuration (copiée depuis config-business.json)
|
||||
│ │ └── radar/ # Blips organisés par release
|
||||
│ │ └── 2025-01-15/ # Blips de la release actuelle
|
||||
│ └── public/ # Fichiers statiques servis
|
||||
│ ├── team.html # Page équipe (copiée depuis public/)
|
||||
│ └── team-visualization-data.json # Données équipe (copiée depuis public/)
|
||||
├── config.json # Configuration principale du radar (déprécié)
|
||||
├── custom.css # Styles personnalisés
|
||||
├── about.md # Page "À propos" du radar
|
||||
├── package.json # Dépendances Node.js
|
||||
├── Dockerfile # Dockerfile alternatif (racine)
|
||||
├── Dockerfile.business # Dockerfile pour le radar business
|
||||
├── docker-compose.yml # Docker Compose alternatif (racine)
|
||||
├── docker-compose.business.yml # Docker Compose pour le radar business
|
||||
├── docker-entrypoint.sh # Script d'entrée Docker
|
||||
└── .drone.yml # Configuration CI/CD Drone
|
||||
```
|
||||
|
||||
## Architecture technique
|
||||
|
||||
### Framework de base
|
||||
|
||||
Le projet utilise le framework **aoe_technology_radar** qui est basé sur :
|
||||
- **Next.js** : Framework React pour le rendu côté serveur et la génération statique
|
||||
- **React** : Bibliothèque JavaScript pour l'interface utilisateur
|
||||
- **TypeScript** : Typage statique pour JavaScript
|
||||
|
||||
### Processus de build
|
||||
|
||||
1. **Injection des données** : Le script `scripts/build-radar.js` copie :
|
||||
- `radar-business/config-business.json` → `radar-app/data/config.json`
|
||||
- `radar-business/2025-01-15/` → `radar-app/data/radar/2025-01-15/`
|
||||
- `public/*` → `radar-app/public/`
|
||||
- Génère `team-visualization-data.json` et le copie dans `radar-app/public/`
|
||||
2. **Modifications personnalisées** :
|
||||
- Création de `radar-app/src/pages/team.tsx` (page Next.js pour `/team`)
|
||||
- Modification de `radar-app/src/components/Navigation/Navigation.tsx` (ajout du lien Équipe)
|
||||
- Modification de `radar-app/src/pages/_document.tsx` (chargement du script team-block-script.js)
|
||||
3. **Build Next.js** : `cd radar-app && npm run build:data && npm run build` génère les fichiers statiques
|
||||
4. **Output** : Fichiers statiques dans `radar-app/out/` copiés vers `build/` à la racine
|
||||
|
||||
### Modifications personnalisées
|
||||
|
||||
Le projet apporte plusieurs modifications au framework de base :
|
||||
|
||||
#### 1. Page Equipe (`/team`)
|
||||
|
||||
- **Script principal** : `public/team-block-script.js` (injection du contenu et visualisations)
|
||||
- **Route Next.js** : `radar-app/src/pages/team.tsx` (page vide, le script remplace le contenu)
|
||||
- **Chargement** : Le script est charge via `_document.tsx` avec `strategy="beforeInteractive"`
|
||||
- **Bibliotheques** : Cytoscape.js et ECharts charges depuis CDN
|
||||
- **Donnees** : `public/team-visualization-data.json` genere par `scripts/generate-team-visualization-data.js`
|
||||
- **Layout** : Utilise le layout `cose` integre a Cytoscape (pas de plugin externe)
|
||||
|
||||
#### 2. Navigation modifiée
|
||||
|
||||
- **Fichier modifié** : `radar-app/src/components/Navigation/Navigation.tsx`
|
||||
- **Modification** : Ajout du lien "👥 Équipe" vers `/team`
|
||||
- **Méthode** : Script Python dans `Dockerfile.business` qui :
|
||||
- Supprime tous les liens Équipe existants (évite les doublons)
|
||||
- Ajoute un seul lien Équipe après le lien "Vue d'ensemble"
|
||||
|
||||
#### 3. Scripts JavaScript désactivés
|
||||
|
||||
Les scripts suivants ont été désactivés pour éviter les doublons de liens :
|
||||
- `public/strategie-script.js` : `addLinksToHeader()` désactivée
|
||||
- `public/strategie-link.js` : `addStrategyLinkToHeader()` désactivée
|
||||
|
||||
Tous les liens de navigation sont maintenant gérés uniquement par `Navigation.tsx`.
|
||||
|
||||
## Format des fichiers radar
|
||||
|
||||
Chaque technologie (blip) est définie dans un fichier Markdown avec un en-tête YAML front matter :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: technologies-differentiantes|technologies-commodite|technologies-risque|technologies-emergentes
|
||||
tags: [tag1, tag2]
|
||||
businessImpact: high|medium|low
|
||||
costToReplace: 0
|
||||
revenueImpact: indirect
|
||||
riskLevel: medium
|
||||
competencyLevel: beginner
|
||||
maintenanceCost: 0
|
||||
differentiation: high
|
||||
teamCoverage: 1
|
||||
skillGap: high
|
||||
---
|
||||
|
||||
Description de la technologie en Markdown.
|
||||
```
|
||||
|
||||
### Métadonnées
|
||||
|
||||
- **title** : Nom de la technologie
|
||||
- **ring** : Anneau du radar (adopt, trial, assess, hold)
|
||||
- **quadrant** : Quadrant du radar (business)
|
||||
- **tags** : Tags pour le filtrage (optionnel)
|
||||
- **Métadonnées business** : Voir [guide-radar-business.md](./guide-radar-business.md)
|
||||
|
||||
## Flux de traitement
|
||||
|
||||
1. **Lecture des fichiers** : Le framework lit tous les fichiers `.md` dans `radar-business/2025-01-15/`
|
||||
2. **Parsing** : Extraction des métadonnées YAML et du contenu Markdown
|
||||
3. **Génération** : Création d'une application React statique
|
||||
4. **Build** : Compilation en fichiers HTML/CSS/JS statiques
|
||||
5. **Serve** : Service via un serveur web statique (`serve` package)
|
||||
|
||||
## Dépendances principales
|
||||
|
||||
- **radar-app/** : Framework principal (code vendu dans le repo, basé sur aoe_technology_radar)
|
||||
- **Node.js** : Runtime JavaScript (version 20+)
|
||||
- **npm** : Gestionnaire de paquets
|
||||
- **gray-matter** : Parsing YAML front matter
|
||||
- **postcss** : Traitement CSS
|
||||
|
||||
## Configuration
|
||||
|
||||
La configuration principale se trouve dans `radar-business/config-business.json` et définit :
|
||||
- Les quadrants et leurs descriptions
|
||||
- Les anneaux (rings) et leurs significations
|
||||
- Les couleurs et le style
|
||||
- Les options d'affichage
|
||||
- Les métadonnées du site
|
||||
|
||||
Voir [configuration.md](./configuration.md) pour plus de détails.
|
||||
|
||||
## Radar Technologique Laplank
|
||||
|
||||
Le Radar Technologique Laplank est un tech radar classique avec :
|
||||
|
||||
- **Configuration spécifique** : `radar-business/config-business.json`
|
||||
- **Quadrants business** : Technologies Différenciantes, Commodité, Risque, Émergentes
|
||||
- **Anneaux classiques** : Hold, Assess, Trial, Adopt
|
||||
- **Historique par release** : Organisation des technologies par date (radar-business/YYYY-MM-DD/)
|
||||
- **Pages de stratégie** : Pages dynamiques générées via `public/strategie-script.js`
|
||||
- **Protection par mot de passe** : Authentification client-side (mot de passe : `laplank-radar`)
|
||||
- **Base path** : `/` (racine, pas de sous-chemin)
|
||||
- **Page Équipe** : `/team` avec visualisations interactives
|
||||
|
||||
### Scripts de stratégie
|
||||
|
||||
Le fichier `public/strategie-script.js` contient :
|
||||
- La logique de protection par mot de passe
|
||||
- La conversion Markdown vers HTML pour les pages de stratégie
|
||||
- Le contenu des trois pages de stratégie :
|
||||
- Stratégie d'Évolution Technique
|
||||
- Stratégie Business
|
||||
- Opportunités DataViz Expert
|
||||
|
||||
**Note** : Les fonctions d'ajout de liens dans le header ont été désactivées pour éviter les doublons. Tous les liens sont maintenant gérés par `Navigation.tsx`.
|
||||
|
||||
## Build et déploiement
|
||||
|
||||
Le projet utilise plusieurs commandes :
|
||||
- `npm run build` : Génère les fichiers statiques du radar principal
|
||||
- `npm run serve` : Lance un serveur de développement du radar principal (port 3000)
|
||||
- `npm run serve-business` : Lance un serveur de développement du radar business (port 3006)
|
||||
- `npm run extract-tech` : Extrait les technologies depuis la documentation
|
||||
- `npm run analyze-business` : Analyse les métriques business
|
||||
|
||||
Le déploiement se fait via Docker avec plusieurs configurations selon l'environnement :
|
||||
- **Radar principal** : Via `docker/Dockerfile` ou `Dockerfile`
|
||||
- **Radar Technologique Laplank** : Via `Dockerfile.business` et `docker-compose.business.yml` (Portainer)
|
||||
|
||||
Voir [deploiement.md](./deploiement.md) pour plus de détails.
|
||||
|
||||
## Génération des données équipe
|
||||
|
||||
Le script `scripts/generate-team-visualization-data.js` :
|
||||
- Lit les profils d'équipe depuis `docs/data/team/*.md`
|
||||
- Lit les technologies depuis `radar-business/2025-01-15/*.md`
|
||||
- Génère `public/team-visualization-data.json` avec :
|
||||
- Données réseau (nodes/edges) pour Cytoscape.js
|
||||
- Matrice de congestion pour ECharts (technologies `adopt` uniquement - anciennement "core")
|
||||
- Équipe de genèse MVP avec statistiques (basée sur les technologies `adopt`)
|
||||
|
||||
Ce fichier est utilisé par `public/team.html` pour les visualisations interactives.
|
||||
|
||||
## Structure des profils équipe
|
||||
|
||||
Les profils d'équipe sont stockés dans `docs/data/team/*.md` avec le format suivant :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: "pseudo"
|
||||
fullName: "Nom complet"
|
||||
role: "Rôle"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
projects:
|
||||
- "Projet1"
|
||||
---
|
||||
```
|
||||
|
||||
Voir [guide-page-equipe.md](./guide-page-equipe.md) pour plus de détails.
|
||||
223
docs/app/configuration.md
Normal file
223
docs/app/configuration.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Configuration
|
||||
|
||||
## Fichier config-business.json
|
||||
|
||||
Le fichier `radar-business/config-business.json` contient toute la configuration du Radar Technologique Laplank. Il définit l'apparence, le comportement et la structure du radar.
|
||||
|
||||
## Structure de configuration
|
||||
|
||||
### Paramètres de base
|
||||
|
||||
```json
|
||||
{
|
||||
"basePath": "",
|
||||
"baseUrl": "",
|
||||
"editUrl": "https://git.open.us.org/syoul/TechradarDev/_edit/main/radar-business/{release}/{id}.md",
|
||||
"logoFile": "logo.svg",
|
||||
"jsFile": "/strategie-script.js"
|
||||
}
|
||||
```
|
||||
|
||||
- **basePath** : Chemin de base pour l'application (vide `""` pour servir à la racine `/`)
|
||||
- **baseUrl** : URL de base du site
|
||||
- **editUrl** : Template d'URL pour éditer les fichiers (utilise {release} et {id})
|
||||
- **logoFile** : Nom du fichier logo dans `public/`
|
||||
- **jsFile** : Fichier JavaScript personnalisé à charger (`/strategie-script.js` pour le radar business)
|
||||
|
||||
### Options d'affichage (toggles)
|
||||
|
||||
```json
|
||||
{
|
||||
"toggles": {
|
||||
"showChart": true,
|
||||
"showTagFilter": true,
|
||||
"showQuadrantList": true,
|
||||
"showEmptyRings": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **showChart** : Affiche le graphique radar interactif
|
||||
- **showTagFilter** : Active le filtre par tags
|
||||
- **showQuadrantList** : Affiche la liste des quadrants
|
||||
- **showEmptyRings** : Affiche les anneaux vides
|
||||
|
||||
### Sections
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": ["radar", "tags", "list"]
|
||||
}
|
||||
```
|
||||
|
||||
Définit l'ordre des sections dans l'interface.
|
||||
|
||||
### Couleurs
|
||||
|
||||
```json
|
||||
{
|
||||
"colors": {
|
||||
"foreground": "#fff",
|
||||
"background": "#1a4d3a",
|
||||
"highlight": "#2ecc71",
|
||||
"content": "#fff",
|
||||
"text": "#575757",
|
||||
"link": "#2ecc71",
|
||||
"border": "rgba(255, 255, 255, 0.1)",
|
||||
"tag": "rgba(255, 255, 255, 0.1)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Personnalisation des couleurs de l'interface avec un thème vert.
|
||||
|
||||
### Quadrants Business
|
||||
|
||||
Les quadrants définissent les quatre catégories principales selon l'impact business :
|
||||
|
||||
1. **Technologies Différenciantes** : Créent un avantage concurrentiel et de la valeur différenciante
|
||||
2. **Technologies de Commodité** : Nécessaires mais non différenciantes, à optimiser pour réduire les coûts
|
||||
3. **Technologies à Risque** : Obsolètes, coûteuses ou présentant des risques, à migrer ou remplacer
|
||||
4. **Technologies Émergentes** : Opportunités futures, à évaluer et potentiellement adopter
|
||||
|
||||
Chaque quadrant a :
|
||||
- **id** : Identifiant unique (technologies-differentiantes, technologies-commodite, technologies-risque, technologies-emergentes)
|
||||
- **title** : Titre affiché
|
||||
- **description** : Description du quadrant
|
||||
- **color** : Couleur associée
|
||||
|
||||
### Anneaux (Rings)
|
||||
|
||||
Les anneaux classifient les technologies selon leur niveau d'adoption. Le Radar Technologique Laplank utilise les anneaux classiques :
|
||||
|
||||
1. **Adopt** : Technologies recommandées et utilisées avec succès en production. Stables, éprouvées, peuvent être adoptées en toute confiance pour de nouveaux projets.
|
||||
2. **Trial** : Technologies à essayer. Prometteuses et testées avec succès dans certains contextes. À considérer pour de nouveaux projets.
|
||||
3. **Assess** : Technologies à évaluer. Prometteuses mais nécessitent une évaluation approfondie avant adoption. À surveiller et tester.
|
||||
4. **Hold** : Technologies à éviter ou à remplacer. Présentent des risques, sont obsolètes ou ne sont plus recommandées. À éviter pour de nouveaux projets.
|
||||
|
||||
Chaque anneau a :
|
||||
- **id** : Identifiant unique (adopt, trial, assess, hold)
|
||||
- **title** : Titre affiché
|
||||
- **description** : Description de l'anneau
|
||||
- **color** : Couleur associée
|
||||
- **radius** : Rayon dans le graphique (0-1)
|
||||
- **strokeWidth** : Épaisseur du trait
|
||||
|
||||
**Important** : Tous les blips doivent utiliser ces rings (adopt|trial|assess|hold). Les anciens rings (core, strategic, support) ne sont plus utilisés.
|
||||
|
||||
### Flags (Drapeaux)
|
||||
|
||||
Les flags marquent les changements entre versions :
|
||||
|
||||
- **new** : Nouveau dans cette version (couleur : #f1235a)
|
||||
- **changed** : Modifié récemment (couleur : #40a7d1)
|
||||
- **default** : Inchangé
|
||||
|
||||
### Graphique
|
||||
|
||||
```json
|
||||
{
|
||||
"chart": {
|
||||
"size": 800,
|
||||
"blipSize": 12
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **size** : Taille du graphique en pixels
|
||||
- **blipSize** : Taille des points (blips) sur le graphique
|
||||
|
||||
### Labels (Textes)
|
||||
|
||||
Les labels permettent de personnaliser tous les textes de l'interface, notamment :
|
||||
- Titre du site : "Radar Technologique Laplank"
|
||||
- Textes des pages
|
||||
- Messages d'erreur
|
||||
- Placeholders
|
||||
- Footer
|
||||
|
||||
## Personnalisation
|
||||
|
||||
### Modifier les couleurs
|
||||
|
||||
Éditez la section `colors` dans `radar-business/config-business.json` avec les codes hexadécimaux souhaités.
|
||||
|
||||
### Ajouter un quadrant
|
||||
|
||||
Ajoutez un nouvel objet dans le tableau `quadrants` avec les propriétés requises.
|
||||
|
||||
### Modifier les descriptions
|
||||
|
||||
Les descriptions des quadrants et anneaux peuvent être modifiées directement dans `config-business.json`.
|
||||
|
||||
### Styles personnalisés
|
||||
|
||||
Le fichier `custom.css` permet d'ajouter des styles CSS personnalisés qui seront appliqués à l'application.
|
||||
|
||||
## Configuration du Radar Technologique Laplank
|
||||
|
||||
Le Radar Technologique Laplank utilise une configuration spécifique dans `radar-business/config-business.json` :
|
||||
|
||||
### Différences principales
|
||||
|
||||
- **basePath** : `""` (vide) pour servir à la racine
|
||||
- **jsFile** : `"/strategie-script.js"` pour charger le script de stratégie
|
||||
- **Quadrants business** : Technologies Différenciantes, Commodité, Risque, Émergentes
|
||||
- **Anneaux classiques** : Hold, Assess, Trial, Adopt
|
||||
- **Couleurs** : Thème vert (`#1a4d3a` pour le background, `#2ecc71` pour les accents)
|
||||
|
||||
### Script de strategie
|
||||
|
||||
Le fichier `public/strategie-script.js` est charge automatiquement et fournit :
|
||||
- Protection par mot de passe (authentification client-side)
|
||||
- Pages de strategie dynamiques (Markdown converti en HTML)
|
||||
|
||||
**Note** : Les fonctions d'ajout de liens dans le header ont ete desactivees pour eviter les doublons. Tous les liens sont maintenant geres par `Navigation.tsx`.
|
||||
|
||||
### Script de la page equipe
|
||||
|
||||
Le fichier `public/team-block-script.js` est charge via `_document.tsx` et fournit :
|
||||
- Detection de la route `/team`
|
||||
- Remplacement du DOM avec le contenu de la page equipe
|
||||
- Chargement dynamique de Cytoscape.js et ECharts depuis CDN
|
||||
- Initialisation des trois visualisations (graphe reseau, matrice congestion, equipe genese)
|
||||
|
||||
Ce script utilise le layout `cose` integre a Cytoscape (pas de plugin externe necessaire).
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
En Docker, la variable `BASE_PATH` peut être utilisée pour modifier dynamiquement le `basePath` dans `config.json`. Le script `docker-entrypoint.sh` effectue cette modification au démarrage.
|
||||
|
||||
Pour le Radar Technologique Laplank, le `basePath` est fixé à `""` (vide) dans `config-business.json` pour servir l'application à la racine.
|
||||
|
||||
## Tags disponibles
|
||||
|
||||
Les tags suivants sont établis pour classifier les technologies :
|
||||
|
||||
- architecture
|
||||
- security
|
||||
- devops
|
||||
- frontend
|
||||
- agile
|
||||
- coding
|
||||
- quality assurance
|
||||
- ci/cd
|
||||
- ux/ui
|
||||
- documentation
|
||||
- blockchain
|
||||
- infrastructure
|
||||
- dataviz
|
||||
- mobile
|
||||
|
||||
Les tags sont utilisés dans les fichiers Markdown des blips et permettent le filtrage dans l'interface.
|
||||
|
||||
## Migration des rings
|
||||
|
||||
Si vous avez des blips avec les anciens rings (core, strategic, support), ils doivent être migrés vers les rings standards :
|
||||
|
||||
- **core** → **adopt** (technologies fondamentales en production)
|
||||
- **strategic** → **assess** (technologies prometteuses à évaluer)
|
||||
- **support** → **adopt** (technologies utilisées en production)
|
||||
- **trial** → **trial** (déjà correct)
|
||||
|
||||
Le script `scripts/migrate-rings.sh` peut être utilisé pour automatiser cette migration.
|
||||
300
docs/app/contribution.md
Normal file
300
docs/app/contribution.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Guide de contribution
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce guide explique comment contribuer au Technology Radar AJR en ajoutant, modifiant ou supprimant des technologies (blips) et des profils équipe.
|
||||
|
||||
## Processus de contribution
|
||||
|
||||
### 1. Préparer l'environnement
|
||||
|
||||
Voir le [guide de développement](./developpement.md) pour l'installation et la configuration de l'environnement local.
|
||||
|
||||
### 2. Créer une branche
|
||||
|
||||
```bash
|
||||
git checkout -b feature/nom-de-la-technologie
|
||||
```
|
||||
|
||||
### 3. Ajouter ou modifier un blip
|
||||
|
||||
#### Ajouter un nouveau blip
|
||||
|
||||
1. Créer un fichier Markdown dans le dossier de release approprié :
|
||||
```
|
||||
radar-business/2025-01-15/nom-technologie.md
|
||||
```
|
||||
|
||||
2. Utiliser le format standard avec toutes les métadonnées :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: technologies-differentiantes|technologies-commodite|technologies-risque|technologies-emergentes
|
||||
tags: [tag1, tag2]
|
||||
businessImpact: high|medium|low
|
||||
costToReplace: 0
|
||||
revenueImpact: indirect
|
||||
riskLevel: medium
|
||||
competencyLevel: beginner
|
||||
maintenanceCost: 0
|
||||
differentiation: high
|
||||
teamCoverage: 1
|
||||
skillGap: high
|
||||
---
|
||||
|
||||
Description de la technologie.
|
||||
|
||||
## Impact Business
|
||||
|
||||
Description de l'impact sur le business.
|
||||
|
||||
## Coûts
|
||||
|
||||
- Coût de remplacement : 0€
|
||||
- Coût de maintenance annuel : 0€
|
||||
|
||||
## Compétences
|
||||
|
||||
- Nombre de personnes maîtrisant : 1
|
||||
- Membres de l'équipe : pseudo
|
||||
- Niveau moyen : beginner
|
||||
- Risque de compétence manquante : high
|
||||
|
||||
## Recommandations
|
||||
|
||||
Recommandations stratégiques pour cette technologie.
|
||||
```
|
||||
|
||||
Voir `radar-business/FORMAT-BLIP.md` pour le format complet.
|
||||
|
||||
#### Modifier un blip existant
|
||||
|
||||
1. Localiser le fichier dans `radar-business/2025-01-15/`
|
||||
2. Modifier le contenu ou les métadonnées
|
||||
3. Si vous changez le ring ou le quadrant, documenter la raison
|
||||
4. **Important** : Vérifier que le ring est standard (adopt, trial, assess, hold)
|
||||
|
||||
#### Supprimer un blip
|
||||
|
||||
Si une technologie doit être retirée du radar :
|
||||
- La déplacer vers le ring "hold" plutôt que de la supprimer
|
||||
- Ou la supprimer complètement si elle n'est plus pertinente
|
||||
|
||||
### 4. Ajouter ou modifier un profil équipe
|
||||
|
||||
#### Ajouter un profil équipe
|
||||
|
||||
1. Créer un fichier Markdown dans `docs/data/team/` :
|
||||
```
|
||||
docs/data/team/pseudo.md
|
||||
```
|
||||
|
||||
2. Utiliser le format standard :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: "pseudo"
|
||||
fullName: "Nom complet"
|
||||
role: "Rôle"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
projects:
|
||||
- "Projet1"
|
||||
---
|
||||
```
|
||||
|
||||
3. Régénérer les données équipe :
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
### 5. Tester localement
|
||||
|
||||
```bash
|
||||
npm run serve-business
|
||||
```
|
||||
|
||||
Vérifier :
|
||||
- L'affichage correct du blip
|
||||
- Le positionnement dans le bon quadrant et ring
|
||||
- La lisibilité du contenu
|
||||
- Le fonctionnement des tags
|
||||
- La page équipe (`/team`) affiche correctement les données
|
||||
|
||||
### 6. Commiter les changements
|
||||
|
||||
```bash
|
||||
git add radar-business/2025-01-15/nom-technologie.md
|
||||
git add docs/data/team/pseudo.md
|
||||
git add public/team-visualization-data.json
|
||||
git commit -m "feat: ajouter [technologie] au quadrant [quadrant]"
|
||||
```
|
||||
|
||||
### 7. Pousser et créer une pull request
|
||||
|
||||
```bash
|
||||
git push origin feature/nom-de-la-technologie
|
||||
```
|
||||
|
||||
Créer une pull request sur le dépôt Git.
|
||||
|
||||
## Guidelines de contenu
|
||||
|
||||
### Choix du ring
|
||||
|
||||
- **Adopt** : Technologies recommandées et utilisées avec succès en production. Stables, éprouvées, peuvent être adoptées en toute confiance pour de nouveaux projets.
|
||||
- **Trial** : Technologies à essayer. Prometteuses et testées avec succès dans certains contextes. À considérer pour de nouveaux projets.
|
||||
- **Assess** : Technologies à évaluer. Prometteuses mais nécessitent une évaluation approfondie avant adoption. À surveiller et tester.
|
||||
- **Hold** : Technologies à éviter ou à remplacer. Présentent des risques, sont obsolètes ou ne sont plus recommandées. À éviter pour de nouveaux projets.
|
||||
|
||||
**Important** : Les anciens rings (core, strategic, support, legacy) ne sont plus utilisés. Tous les blips doivent utiliser les rings standards (adopt, trial, assess, hold).
|
||||
|
||||
### Choix du quadrant
|
||||
|
||||
- **Technologies Différenciantes** : Créent un avantage concurrentiel et de la valeur différenciante
|
||||
- **Technologies de Commodité** : Nécessaires mais non différenciantes, à optimiser pour réduire les coûts
|
||||
- **Technologies à Risque** : Obsolètes, coûteuses, à migrer ou remplacer
|
||||
- **Technologies Émergentes** : Opportunités futures, à évaluer et potentiellement adopter
|
||||
|
||||
### Tags
|
||||
|
||||
Utiliser les tags établis :
|
||||
- architecture
|
||||
- security
|
||||
- devops
|
||||
- frontend
|
||||
- agile
|
||||
- coding
|
||||
- quality assurance
|
||||
- ci/cd
|
||||
- ux/ui
|
||||
- documentation
|
||||
- blockchain
|
||||
- infrastructure
|
||||
- dataviz
|
||||
- mobile
|
||||
|
||||
Ajouter plusieurs tags si la technologie couvre plusieurs domaines.
|
||||
|
||||
### Qualité du contenu
|
||||
|
||||
- **Clarté** : Description claire et concise
|
||||
- **Pertinence** : Focus sur l'utilisation dans l'écosystème Duniter/Ğ1
|
||||
- **Objectivité** : Présenter les avantages et inconvénients
|
||||
- **Concision** : Rester factuel et éviter les détails superflus
|
||||
- **Métadonnées complètes** : Remplir toutes les métadonnées business
|
||||
|
||||
## Format des commits
|
||||
|
||||
Utiliser des messages de commit clairs :
|
||||
|
||||
```
|
||||
feat: ajouter [technologie] au quadrant [quadrant]
|
||||
fix: corriger la description de [technologie]
|
||||
update: déplacer [technologie] de trial à adopt
|
||||
docs: améliorer la documentation de [technologie]
|
||||
feat(team): ajouter profil [pseudo]
|
||||
fix(team): mettre à jour compétences de [pseudo]
|
||||
```
|
||||
|
||||
## Créer une nouvelle release
|
||||
|
||||
Quand créer une nouvelle release :
|
||||
|
||||
1. **Périodicité** : Généralement tous les 3-6 mois
|
||||
2. **Changements significatifs** : Plusieurs nouveaux blips ou changements majeurs
|
||||
3. **Événements** : Après des évaluations importantes
|
||||
|
||||
### Processus de release
|
||||
|
||||
1. Créer un nouveau dossier avec la date :
|
||||
```bash
|
||||
mkdir radar-business/2025-07-15
|
||||
```
|
||||
|
||||
2. Copier les blips pertinents depuis la release précédente
|
||||
|
||||
3. Ajouter les nouveaux blips
|
||||
|
||||
4. Mettre à jour les blips existants si nécessaire (changement de ring, quadrant, description)
|
||||
|
||||
5. **Migrer les rings si nécessaire** : S'assurer que tous les blips utilisent les rings standards
|
||||
|
||||
6. Documenter les changements majeurs
|
||||
|
||||
## Migration des rings
|
||||
|
||||
Si vous avez des blips avec les anciens rings, utilisez ce mapping :
|
||||
|
||||
- **core** → **adopt** : Technologies fondamentales en production
|
||||
- **strategic** → **assess** : Technologies prometteuses à évaluer
|
||||
- **support** → **adopt** : Technologies utilisées en production
|
||||
- **trial** → **trial** : Déjà correct
|
||||
- **legacy** → **hold** : Technologies obsolètes à remplacer
|
||||
|
||||
Le script `scripts/migrate-rings.sh` peut être utilisé pour automatiser cette migration.
|
||||
|
||||
## Review process
|
||||
|
||||
Les contributions sont revues pour :
|
||||
|
||||
- **Exactitude** : Les informations sont correctes
|
||||
- **Pertinence** : La technologie est pertinente pour l'écosystème
|
||||
- **Format** : Le format Markdown est correct
|
||||
- **Classification** : Le ring et quadrant sont appropriés
|
||||
- **Rings standards** : Utilisation des rings standards (adopt, trial, assess, hold)
|
||||
- **Métadonnées** : Toutes les métadonnées business sont remplies
|
||||
- **Qualité** : Le contenu est clair et utile
|
||||
|
||||
## Questions fréquentes
|
||||
|
||||
### Puis-je ajouter une technologie que je n'ai pas encore utilisée ?
|
||||
|
||||
Non. Le radar ne contient que des technologies testées au moins une fois par l'équipe.
|
||||
|
||||
### Comment décider entre deux quadrants ?
|
||||
|
||||
Choisir le quadrant le plus approprié selon l'impact business. Si c'est ambigu, discuter avec l'équipe.
|
||||
|
||||
### Puis-je modifier un blip d'une release précédente ?
|
||||
|
||||
Généralement non. Les releases précédentes sont figées. Créer un nouveau blip dans la release actuelle si nécessaire.
|
||||
|
||||
### Comment gérer les technologies obsolètes ?
|
||||
|
||||
Les déplacer vers le ring "hold" avec une explication de pourquoi elles ne sont plus recommandées.
|
||||
|
||||
### Quels rings dois-je utiliser ?
|
||||
|
||||
Toujours utiliser les rings standards : **adopt**, **trial**, **assess**, **hold**. Les anciens rings (core, strategic, support, legacy) ne sont plus utilisés.
|
||||
|
||||
### Comment mettre à jour les données équipe ?
|
||||
|
||||
1. Modifier les fichiers dans `docs/data/team/*.md`
|
||||
2. Régénérer les données : `node scripts/generate-team-visualization-data.js`
|
||||
3. Commiter les deux fichiers (profils + données JSON)
|
||||
|
||||
## Ressources
|
||||
|
||||
- [Guide de développement](./developpement.md)
|
||||
- [Configuration](./configuration.md)
|
||||
- [Architecture](./architecture.md)
|
||||
- [Format des blips](../radar-business/FORMAT-BLIP.md)
|
||||
- [Guide page équipe](./guide-page-equipe.md)
|
||||
- Framework source : https://github.com/AOEpeople/aoe_technology_radar
|
||||
|
||||
## Contact
|
||||
|
||||
Pour toute question sur les contributions, contacter l'équipe AJR ou ouvrir une issue sur le dépôt Git.
|
||||
400
docs/app/deploiement.md
Normal file
400
docs/app/deploiement.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# Guide de déploiement
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le projet peut être déployé de plusieurs façons :
|
||||
- Docker Compose (recommandé pour la production)
|
||||
- Docker simple
|
||||
- Build statique avec serveur web
|
||||
- Portainer (pour le Radar Business)
|
||||
|
||||
## Déploiement avec Docker
|
||||
|
||||
### Configuration Docker
|
||||
|
||||
Le projet contient plusieurs configurations Docker :
|
||||
|
||||
- `docker/Dockerfile` : Dockerfile principal avec multi-stage build
|
||||
- `docker/docker-compose.yml` : Configuration de base
|
||||
- `docker/docker-compose.labels.yml` : Labels pour le reverse proxy
|
||||
- `docker/docker-compose.local.yml` : Configuration pour développement local
|
||||
- `Dockerfile` (racine) : Dockerfile alternatif
|
||||
- `docker-compose.yml` (racine) : Docker Compose alternatif
|
||||
- `Dockerfile.business` : Dockerfile spécifique pour le Radar Business
|
||||
- `docker-compose.business.yml` : Docker Compose pour le Radar Business (Portainer)
|
||||
|
||||
### Build de l'image Docker
|
||||
|
||||
#### Avec le Dockerfile principal
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Avec build args
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg BASE_PATH="/techradar" \
|
||||
--build-arg UID=1000 \
|
||||
--build-arg GID=1000 \
|
||||
-f docker/Dockerfile \
|
||||
-t techradar:latest \
|
||||
.
|
||||
```
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
- **BASE_PATH** : Chemin de base pour l'application (défaut: `/`)
|
||||
- **UID** : User ID pour l'utilisateur dans le conteneur (défaut: 1000)
|
||||
- **GID** : Group ID pour l'utilisateur dans le conteneur (défaut: 1000)
|
||||
|
||||
### Démarrage avec Docker Compose
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
L'application sera accessible sur le port 3000.
|
||||
|
||||
### Configuration du basePath
|
||||
|
||||
Le script `docker-entrypoint.sh` modifie dynamiquement le `basePath` dans `config.json` au démarrage du conteneur en utilisant la variable d'environnement `BASE_PATH`.
|
||||
|
||||
## Déploiement du Radar Technologique Laplank avec Portainer
|
||||
|
||||
Le Radar Technologique Laplank est déployé via Portainer en utilisant une stack Docker Compose.
|
||||
|
||||
### Configuration Portainer
|
||||
|
||||
1. **Créer une nouvelle stack** dans Portainer
|
||||
2. **Nom de la stack** : `laplank-radar-technologique` (ou autre nom)
|
||||
3. **Méthode** : Git Repository
|
||||
4. **Repository URL** : `https://git.open.us.org/AJR/TechradarDev.git`
|
||||
5. **Reference** : `refs/heads/dev-tech` (branche actuelle)
|
||||
6. **Compose path** : `docker-compose.business.yml`
|
||||
|
||||
**Note** : Si le dépôt est privé, utiliser un Personal Access Token dans l'URL :
|
||||
- `https://<token>@git.open.us.org/AJR/TechradarDev.git`
|
||||
|
||||
### Configuration Docker Compose Laplank
|
||||
|
||||
Le fichier `docker-compose.business.yml` configure :
|
||||
- **Port** : `3006:3000` (port 3006 de l'hôte mappé vers le port 3000 du conteneur)
|
||||
- **Container name** : `laplank-radar-business`
|
||||
- **Image** : Construite depuis `Dockerfile.business` lors du déploiement
|
||||
- **Environnement** : `NODE_ENV=production`
|
||||
- **Restart policy** : `unless-stopped` (redémarre automatiquement en cas d'arrêt)
|
||||
|
||||
**Accès à l'application :**
|
||||
- URL : `http://<votre-serveur>:3006`
|
||||
- Mot de passe : `laplank-radar`
|
||||
|
||||
### Dockerfile Business
|
||||
|
||||
Le `Dockerfile.business` effectue les opérations suivantes :
|
||||
|
||||
1. **Installation des dépendances** :
|
||||
- Node.js 20 Alpine
|
||||
- Git et Python3 pour les scripts
|
||||
- Variables d'environnement pour désactiver Husky
|
||||
|
||||
2. **Installation des dépendances** :
|
||||
- Installation des dépendances racine (pour scripts: generate-team-visualization-data, etc.)
|
||||
- Installation des dépendances dans `radar-app/` (Next.js et dépendances du framework)
|
||||
- Désactivation du script `prepare` (husky) dans `radar-app/package.json`
|
||||
|
||||
3. **Configuration des données** :
|
||||
- Purge des données de démo : `rm -rf radar-app/data/radar/*`
|
||||
- Copie des blips business : `radar-business/2025-01-15/*` → `radar-app/data/radar/2025-01-15/`
|
||||
- Copie de la config : `radar-business/config-business.json` → `radar-app/data/config.json`
|
||||
- Copie des fichiers publics : `public/*` → `radar-app/public/`
|
||||
- Génération et copie de `team-visualization-data.json` dans `radar-app/public/`
|
||||
|
||||
4. **Modifications personnalisees** :
|
||||
- Creation de `radar-app/src/pages/team.tsx` (page Next.js vide pour `/team`)
|
||||
- Modification de `radar-app/src/pages/_document.tsx` via script Python :
|
||||
- Ajout du chargement de `team-block-script.js` avec `strategy="beforeInteractive"`
|
||||
- Modification de `radar-app/src/components/Navigation/Navigation.tsx` via script Python :
|
||||
- Suppression de tous les liens Equipe existants (evite les doublons)
|
||||
- Ajout d'un seul lien "Equipe" apres le lien "Vue d'ensemble"
|
||||
|
||||
5. **Build Next.js** :
|
||||
- `cd radar-app && npm run build:data` : Génère les données du radar
|
||||
- `cd radar-app && npm run build` : Build de l'application Next.js
|
||||
|
||||
6. **Post-build** :
|
||||
- Copie des fichiers additionnels (`_team-content`, `team-visualization-data.json`, `team/`) depuis `radar-app/public/` vers `radar-app/out/`
|
||||
|
||||
7. **Demarrage** :
|
||||
- Execution de `scripts/start-business.sh` qui :
|
||||
- Verifie que `team-visualization-data.json` est dans `out/`
|
||||
- Le copie depuis `public/` si necessaire
|
||||
- Demarre le serveur statique `serve` sur le port 3000 (sans `--single`)
|
||||
|
||||
### Scripts Python pour les modifications
|
||||
|
||||
#### Script pour Navigation.tsx
|
||||
|
||||
Le script `docker/add_team_link.py` :
|
||||
|
||||
1. **Verifie l'existence du fichier** : `radar-app/src/components/Navigation/Navigation.tsx`
|
||||
2. **Supprime tous les liens Equipe existants** : Evite les doublons meme si le script s'execute plusieurs fois
|
||||
3. **Ajoute un seul lien Equipe** : Apres le lien "Vue d'ensemble"
|
||||
4. **Verifie le resultat** : S'assure qu'il n'y a qu'un seul lien apres l'operation
|
||||
|
||||
Le script shell `docker/add_team_link.sh` orchestre l'execution et verifie le resultat.
|
||||
|
||||
#### Script pour _document.tsx
|
||||
|
||||
Le script `docker/patch_document.py` :
|
||||
|
||||
1. **Ajoute l'import de Script** : Si pas deja present dans le fichier
|
||||
2. **Modifie le composant Head** : Ajoute le chargement de `team-block-script.js`
|
||||
3. **Strategie beforeInteractive** : Le script est charge avant le rendu Next.js
|
||||
|
||||
### Authentification Git pour Portainer
|
||||
|
||||
Si le dépôt est privé, utiliser un **Personal Access Token** (Gitea) :
|
||||
1. Créer un token dans Gitea avec les permissions de lecture
|
||||
2. Utiliser l'URL avec le token : `https://<token>@git.open.us.org/AJR/TechradarDev.git`
|
||||
3. Exemple : `https://glpat-xxxxxxxxxxxx@git.open.us.org/AJR/TechradarDev.git`
|
||||
|
||||
**Configuration complète pour dépôt privé :**
|
||||
- **Repository URL** : `https://<token>@git.open.us.org/AJR/TechradarDev.git`
|
||||
- **Reference** : `refs/heads/dev-tech`
|
||||
- **Compose path** : `docker-compose.business.yml`
|
||||
|
||||
### Mise à jour
|
||||
|
||||
Pour mettre à jour le Radar Technologique Laplank dans Portainer :
|
||||
|
||||
**⚠️ IMPORTANT : Pour que les mises à jour soient effectives, il faut forcer le rebuild sans cache !**
|
||||
|
||||
**Option 1 : Rebuild avec --no-cache (RECOMMANDÉ)**
|
||||
1. Aller dans **Stacks** → Sélectionner la stack `laplank-radar-technologique`
|
||||
2. Cliquer sur **Editor**
|
||||
3. **Cocher la case "Always pull image"** (si disponible)
|
||||
4. **Cocher la case "Rebuild"** ou utiliser l'option "Rebuild the stack"
|
||||
5. **Dans les options avancées, cocher "No cache"** ou utiliser `--no-cache` dans les build args
|
||||
6. Cliquer sur **Update the stack**
|
||||
7. Portainer va reconstruire l'image complètement sans utiliser le cache
|
||||
|
||||
**Option 2 : Rebuild manuel via l'interface**
|
||||
1. Aller dans **Stacks** → Sélectionner la stack `laplank-radar-technologique`
|
||||
2. Cliquer sur **Editor**
|
||||
3. Cliquer sur **Update the stack**
|
||||
4. **Avant de confirmer**, dans les options de build, ajouter `--no-cache` ou cocher "No cache"
|
||||
5. Confirmer la mise à jour
|
||||
|
||||
**Option 3 : Supprimer l'image et rebuild (si les options ci-dessus ne fonctionnent pas)**
|
||||
1. Aller dans **Containers** → Sélectionner `laplank-radar-technolologique`
|
||||
2. Cliquer sur **Stop** pour arrêter le conteneur
|
||||
3. Aller dans **Images** → Trouver l'image de la stack
|
||||
4. Cliquer sur **Remove** pour supprimer l'image
|
||||
5. Retourner dans **Stacks** → Sélectionner la stack
|
||||
6. Cliquer sur **Editor** → **Update the stack**
|
||||
7. L'image sera reconstruite depuis zéro
|
||||
|
||||
**Vérification après mise à jour :**
|
||||
- Vérifier les logs : **Containers** → `laplank-radar-technolologique` → **Logs**
|
||||
- Tester l'application : `http://<votre-serveur>:3006`
|
||||
- Vérifier que les changements sont visibles (par exemple, le contenu de `about.md` ou `custom.css`)
|
||||
- Vérifier qu'il n'y a qu'un seul lien "Équipe" dans la navigation
|
||||
|
||||
**Pourquoi le cache pose problème ?**
|
||||
Docker utilise un système de cache par couches. Si les fichiers copiés n'ont pas changé selon l'algorithme de détection de Docker, il réutilise les couches en cache. C'est pourquoi il faut forcer un rebuild complet avec `--no-cache` pour garantir que tous les fichiers sont bien copiés et que l'application est reconstruite avec les dernières modifications.
|
||||
|
||||
## Déploiement statique
|
||||
|
||||
### Build des fichiers statiques
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Les fichiers sont générés dans le répertoire `build/`.
|
||||
|
||||
### Servir avec un serveur web
|
||||
|
||||
#### Nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name coeurbox.syoul.fr;
|
||||
root /chemin/vers/build;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Apache
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName coeurbox.syoul.fr
|
||||
DocumentRoot /chemin/vers/build
|
||||
|
||||
<Directory /chemin/vers/build>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## Déploiement avec Drone CI
|
||||
|
||||
Le projet est configuré pour le déploiement automatique via Drone CI (`.drone.yml`).
|
||||
|
||||
### Pipeline de déploiement
|
||||
|
||||
1. **Build** : Construction de l'image Docker
|
||||
2. **Déploiement** : Lancement du conteneur avec Docker Compose
|
||||
3. **Notification** : Envoi d'une notification Telegram
|
||||
|
||||
### Configuration Drone
|
||||
|
||||
Le pipeline utilise :
|
||||
- Variables d'environnement dynamiques basées sur le dépôt Git
|
||||
- Labels pour le reverse proxy (Traefik)
|
||||
- Notifications Telegram en cas de succès/échec
|
||||
|
||||
### Variables d'environnement Drone
|
||||
|
||||
- `DRONE_REPO_OWNER` : Propriétaire du dépôt
|
||||
- `DRONE_REPO_NAME` : Nom du dépôt
|
||||
- `DRONE_COMMIT_BRANCH` : Branche du commit
|
||||
|
||||
Ces variables sont utilisées pour générer le `BASE_PATH` dynamiquement.
|
||||
|
||||
## Déploiement en production
|
||||
|
||||
### Étapes recommandées
|
||||
|
||||
1. **Préparer l'environnement**
|
||||
```bash
|
||||
git clone https://git.open.us.org/AJR/TechradarDev.git
|
||||
cd TechradarDev
|
||||
```
|
||||
|
||||
2. **Configurer les variables**
|
||||
- Définir `BASE_PATH` selon votre configuration
|
||||
- Ajuster les ports si nécessaire
|
||||
|
||||
3. **Build et démarrage**
|
||||
```bash
|
||||
cd docker
|
||||
docker compose -f docker-compose.yml -f docker-compose.labels.yml up -d --build
|
||||
```
|
||||
|
||||
4. **Vérifier le déploiement**
|
||||
- Accéder à l'URL configurée
|
||||
- Vérifier les logs : `docker compose logs -f`
|
||||
|
||||
### Reverse proxy
|
||||
|
||||
Le projet est configuré pour fonctionner derrière un reverse proxy (Traefik) via les labels dans `docker-compose.labels.yml`.
|
||||
|
||||
### Sécurité
|
||||
|
||||
- Utiliser HTTPS en production
|
||||
- Configurer les headers de sécurité appropriés
|
||||
- Limiter l'accès si nécessaire
|
||||
- Surveiller les logs
|
||||
- Le Radar Business est protégé par un mot de passe client-side
|
||||
|
||||
## Mise à jour
|
||||
|
||||
### Mettre à jour le contenu
|
||||
|
||||
1. Modifier les fichiers dans `radar-business/2025-01-15/`
|
||||
2. Rebuild l'image :
|
||||
```bash
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Mettre à jour les dépendances
|
||||
|
||||
1. Modifier `package.json` si nécessaire
|
||||
2. Rebuild l'image complète avec `--no-cache`
|
||||
|
||||
### Mettre à jour les profils équipe
|
||||
|
||||
1. Modifier les fichiers dans `docs/data/team/*.md`
|
||||
2. Régénérer les données :
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
3. Rebuild l'image Docker
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Logs Docker
|
||||
|
||||
```bash
|
||||
# Voir les logs
|
||||
docker compose logs -f
|
||||
|
||||
# Logs du dernier démarrage
|
||||
docker compose logs --tail=100
|
||||
```
|
||||
|
||||
### Santé de l'application
|
||||
|
||||
**Radar Principal** : Expose le port 3000. Vérifier avec :
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/techradar
|
||||
```
|
||||
|
||||
**Radar Technologique Laplank** : Expose le port 3006 (mappé depuis 3000). Vérifier avec :
|
||||
|
||||
```bash
|
||||
curl http://localhost:3006/
|
||||
```
|
||||
|
||||
Note : Le Radar Technologique Laplank est protégé par un mot de passe, donc la réponse peut être l'écran d'authentification.
|
||||
|
||||
### Vérifications post-déploiement
|
||||
|
||||
1. **Vérifier la navigation** :
|
||||
- Le lien "👥 Équipe" doit apparaître une seule fois
|
||||
- Tous les liens doivent fonctionner
|
||||
|
||||
2. **Vérifier les données** :
|
||||
- Tous les blips doivent être affichés (38 blips)
|
||||
- Les rings doivent être corrects (adopt, trial, assess, hold)
|
||||
|
||||
3. **Vérifier la page équipe** :
|
||||
- `/team` doit être accessible
|
||||
- Les visualisations doivent se charger
|
||||
- Les données doivent être présentes
|
||||
|
||||
## Rollback
|
||||
|
||||
En cas de problème, revenir à une version précédente :
|
||||
|
||||
```bash
|
||||
# Arrêter le conteneur actuel
|
||||
docker compose down
|
||||
|
||||
# Checkout une version précédente
|
||||
git checkout <commit-hash>
|
||||
|
||||
# Rebuild et redémarrer
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Voir [troubleshooting.md](./troubleshooting.md) pour les problèmes courants et leurs solutions.
|
||||
373
docs/app/developpement.md
Normal file
373
docs/app/developpement.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# Guide de développement
|
||||
|
||||
## Prérequis
|
||||
|
||||
- **Node.js** : Version 20 ou supérieure
|
||||
- **npm** : Gestionnaire de paquets Node.js
|
||||
- **Git** : Pour cloner et gérer le dépôt
|
||||
- **Python 3** : Pour les scripts de modification (optionnel, utilisé dans Docker)
|
||||
|
||||
## Installation
|
||||
|
||||
### Cloner le dépôt
|
||||
|
||||
```bash
|
||||
git clone https://git.open.us.org/AJR/TechradarDev.git
|
||||
cd TechradarDev
|
||||
```
|
||||
|
||||
### Installer les dépendances
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Cette commande installe les dépendances racine (pour les scripts utilitaires). Le framework Next.js est déjà présent dans `radar-app/` (code vendu dans le repo).
|
||||
|
||||
## Développement local
|
||||
|
||||
### Démarrer le serveur de développement (Radar Principal)
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3000/techradar
|
||||
|
||||
### Démarrer le serveur de développement (Radar Business)
|
||||
|
||||
```bash
|
||||
npm run serve-business
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3006
|
||||
|
||||
**Note importante** : Le script `serve-business.sh` :
|
||||
- Sauvegarde temporairement `config.json` et le dossier `radar/`
|
||||
- Copie `radar-business/config-business.json` vers `config.json`
|
||||
- Copie `radar-business/2025-01-15/` vers `radar/`
|
||||
- Restaure la configuration originale à la sortie (Ctrl+C)
|
||||
|
||||
Le Radar Technologique Laplank est protégé par un mot de passe : `laplank-radar`
|
||||
|
||||
### Build de production
|
||||
|
||||
Pour générer les fichiers statiques :
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Les fichiers générés sont créés dans le répertoire `build/` (copiés depuis `radar-app/out/`).
|
||||
|
||||
## Structure des fichiers radar
|
||||
|
||||
### Créer un nouveau blip
|
||||
|
||||
1. Créer un nouveau fichier Markdown dans le dossier de release approprié :
|
||||
```
|
||||
radar-business/2025-01-15/nom-technologie.md
|
||||
```
|
||||
|
||||
2. Utiliser le format suivant :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: technologies-differentiantes|technologies-commodite|technologies-risque|technologies-emergentes
|
||||
tags: [tag1, tag2]
|
||||
businessImpact: high|medium|low
|
||||
costToReplace: 0
|
||||
revenueImpact: indirect
|
||||
riskLevel: medium
|
||||
competencyLevel: beginner
|
||||
maintenanceCost: 0
|
||||
differentiation: high
|
||||
teamCoverage: 1
|
||||
skillGap: high
|
||||
---
|
||||
|
||||
Description de la technologie en Markdown.
|
||||
|
||||
## Impact Business
|
||||
|
||||
Description de l'impact sur le business.
|
||||
|
||||
## Coûts
|
||||
|
||||
- Coût de remplacement : 0€
|
||||
- Coût de maintenance annuel : 0€
|
||||
|
||||
## Compétences
|
||||
|
||||
- Nombre de personnes maîtrisant : 1
|
||||
- Membres de l'équipe : pseudo
|
||||
- Niveau moyen : beginner
|
||||
- Risque de compétence manquante : high
|
||||
|
||||
## Recommandations
|
||||
|
||||
Recommandations stratégiques pour cette technologie.
|
||||
```
|
||||
|
||||
### Format des métadonnées
|
||||
|
||||
- **title** (obligatoire) : Nom de la technologie
|
||||
- **ring** (obligatoire) : `adopt`, `trial`, `assess`, ou `hold` (voir [configuration.md](./configuration.md))
|
||||
- **quadrant** (obligatoire) : Un des quatre quadrants définis dans `radar-business/config-business.json`
|
||||
- **tags** (optionnel) : Tableau de tags pour le filtrage
|
||||
- **Métadonnées business** : Voir `radar-business/FORMAT-BLIP.md` pour la liste complète
|
||||
|
||||
**Important** : Tous les blips doivent utiliser les rings standards (adopt, trial, assess, hold). Les anciens rings (core, strategic, support) ne sont plus utilisés.
|
||||
|
||||
### Exemple complet
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Docker"
|
||||
ring: adopt
|
||||
quadrant: technologies-commodite
|
||||
tags: [devops, ci/cd, infrastructure]
|
||||
businessImpact: medium
|
||||
costToReplace: 0
|
||||
revenueImpact: indirect
|
||||
riskLevel: low
|
||||
competencyLevel: intermediate
|
||||
maintenanceCost: 5000
|
||||
differentiation: low
|
||||
teamCoverage: 3
|
||||
skillGap: low
|
||||
---
|
||||
|
||||
Docker est une plateforme de conteneurisation qui permet de packager des applications avec leurs dépendances.
|
||||
|
||||
## Impact Business
|
||||
|
||||
Technologie essentielle pour le déploiement et la gestion des environnements.
|
||||
|
||||
## Coûts
|
||||
|
||||
- Coût de remplacement : 0€ (pas de remplacement prévu)
|
||||
- Coût de maintenance annuel : 5 000€ (licences, support)
|
||||
|
||||
## Compétences
|
||||
|
||||
- Nombre de personnes maîtrisant : 3
|
||||
- Membres de l'équipe : pseudo1, pseudo2, pseudo3
|
||||
- Niveau moyen : intermediate
|
||||
- Risque de compétence manquante : low
|
||||
|
||||
## Recommandations
|
||||
|
||||
Continuer à utiliser Docker pour tous les nouveaux projets. Standardiser les pratiques de conteneurisation.
|
||||
```
|
||||
|
||||
## Modifier un blip existant
|
||||
|
||||
1. Localiser le fichier dans `radar-business/2025-01-15/`
|
||||
2. Modifier le contenu Markdown
|
||||
3. Si nécessaire, modifier les métadonnées (ring, quadrant, tags)
|
||||
4. Tester localement avec `npm run serve-business`
|
||||
|
||||
## Créer une nouvelle release
|
||||
|
||||
1. Créer un nouveau dossier avec la date au format `YYYY-MM-DD` :
|
||||
```bash
|
||||
mkdir radar-business/2025-07-15
|
||||
```
|
||||
|
||||
2. Copier les blips pertinents depuis la release précédente ou créer de nouveaux blips
|
||||
|
||||
3. Mettre à jour les blips existants si nécessaire (changement de ring, quadrant, description)
|
||||
|
||||
## Gestion des profils équipe
|
||||
|
||||
### Créer un profil équipe
|
||||
|
||||
1. Créer un fichier Markdown dans `docs/data/team/` :
|
||||
```
|
||||
docs/data/team/pseudo.md
|
||||
```
|
||||
|
||||
2. Utiliser le format suivant :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: "pseudo"
|
||||
fullName: "Nom complet"
|
||||
role: "Rôle"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Python"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
- "Pédagogie"
|
||||
projects:
|
||||
- "Projet1"
|
||||
- "Projet2"
|
||||
---
|
||||
|
||||
Description du membre de l'équipe.
|
||||
```
|
||||
|
||||
### Générer les données de visualisation
|
||||
|
||||
Après avoir modifié les profils équipe ou les technologies, régénérer les données :
|
||||
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
Ce script génère `public/team-visualization-data.json` utilisé par la page `/team`.
|
||||
|
||||
## Ajouter des images
|
||||
|
||||
1. Placer les images dans `public/images/`
|
||||
2. Référencer dans les fichiers Markdown :
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
## Personnalisation CSS
|
||||
|
||||
Le fichier `custom.css` permet d'ajouter des styles personnalisés. Les styles sont appliqués globalement à l'application.
|
||||
|
||||
## Scripts disponibles
|
||||
|
||||
### Extraction des technologies
|
||||
|
||||
```bash
|
||||
npm run extract-tech
|
||||
# ou
|
||||
node scripts/extract-technologies.js
|
||||
```
|
||||
|
||||
Extrait les technologies depuis `docs/data/technologies-duniter.md` et génère les blips dans `radar-business/2025-01-15/`.
|
||||
|
||||
### Analyse des métriques business
|
||||
|
||||
```bash
|
||||
npm run analyze-business
|
||||
# ou
|
||||
node scripts/analyze-business-metrics.js
|
||||
```
|
||||
|
||||
Analyse les métriques business et génère un rapport dans `docs/data/analyse-strategique.md`.
|
||||
|
||||
### Génération des données équipe
|
||||
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
Génère `public/team-visualization-data.json` à partir des profils équipe et des technologies.
|
||||
|
||||
## Débogage
|
||||
|
||||
### Vérifier les erreurs de format
|
||||
|
||||
Le framework valide les fichiers Markdown. En cas d'erreur :
|
||||
- Vérifier la syntaxe YAML front matter
|
||||
- Vérifier que les valeurs de `ring` et `quadrant` correspondent aux valeurs définies dans `radar-business/config-business.json`
|
||||
- Vérifier la syntaxe Markdown
|
||||
|
||||
### Problèmes courants
|
||||
|
||||
1. **Erreur de parsing YAML** : Vérifier les guillemets et l'indentation
|
||||
2. **Blip non affiché** : Vérifier que le quadrant et le ring sont corrects (adopt, trial, assess, hold)
|
||||
3. **Images non chargées** : Vérifier le chemin relatif depuis `public/`
|
||||
4. **Rings invalides** : Vérifier que tous les blips utilisent les rings standards
|
||||
|
||||
### Vérifier les rings utilisés
|
||||
|
||||
```bash
|
||||
cd radar-business/2025-01-15
|
||||
grep -h "^ring:" *.md | sort | uniq -c
|
||||
```
|
||||
|
||||
Doit retourner uniquement : adopt, trial, assess, hold
|
||||
|
||||
## Workflow recommandé
|
||||
|
||||
1. Créer une branche Git pour vos modifications
|
||||
2. Ajouter/modifier les fichiers radar dans `radar-business/2025-01-15/`
|
||||
3. Modifier les profils équipe dans `docs/data/team/` si nécessaire
|
||||
4. Régénérer les données équipe : `node scripts/generate-team-visualization-data.js`
|
||||
5. Tester localement avec `npm run serve-business`
|
||||
6. Vérifier l'affichage et le formatage
|
||||
7. Commiter et pousser les changements
|
||||
8. Créer une pull request si applicable
|
||||
|
||||
## Commandes utiles
|
||||
|
||||
```bash
|
||||
# Installer les dépendances
|
||||
npm install
|
||||
|
||||
# Démarrer le serveur de développement (business)
|
||||
npm run serve-business
|
||||
|
||||
# Build de production
|
||||
npm run build
|
||||
|
||||
# Extraire les technologies
|
||||
npm run extract-tech
|
||||
|
||||
# Analyser les métriques
|
||||
npm run analyze-business
|
||||
|
||||
# Générer les données équipe
|
||||
node scripts/generate-team-visualization-data.js
|
||||
|
||||
# Vérifier les fichiers modifiés
|
||||
git status
|
||||
|
||||
# Voir les différences
|
||||
git diff
|
||||
```
|
||||
|
||||
## Intégration continue
|
||||
|
||||
Le projet utilise Drone CI pour l'intégration continue. Voir `.drone.yml` pour la configuration.
|
||||
|
||||
Les builds automatiques :
|
||||
- Construisent l'image Docker
|
||||
- Déploient sur l'environnement de test
|
||||
- Envoient des notifications Telegram
|
||||
|
||||
## Tests locaux avant déploiement
|
||||
|
||||
Avant de déployer, vérifier :
|
||||
|
||||
1. **Tous les blips utilisent les rings standards** :
|
||||
```bash
|
||||
cd radar-business/2025-01-15
|
||||
grep -h "^ring:" *.md | sort | uniq
|
||||
```
|
||||
|
||||
2. **Les données équipe sont à jour** :
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
3. **La page équipe fonctionne** :
|
||||
- Démarrer `npm run serve-business`
|
||||
- Accéder à http://localhost:3006/team
|
||||
- Vérifier que les visualisations se chargent
|
||||
|
||||
4. **La navigation est correcte** :
|
||||
- Vérifier qu'il n'y a qu'un seul lien "Équipe"
|
||||
- Vérifier que tous les liens fonctionnent
|
||||
403
docs/app/guide-page-equipe.md
Normal file
403
docs/app/guide-page-equipe.md
Normal file
@@ -0,0 +1,403 @@
|
||||
# Guide de la Page Equipe & Technologies
|
||||
|
||||
## Introduction
|
||||
|
||||
La page **Equipe & Technologies** (`/team`) est une interface de visualisation interactive qui permet d'analyser les competences de l'equipe, d'identifier les congestions (technologies avec peu de personnes) et de composer une equipe de genese pour un MVP.
|
||||
|
||||
Cette page est accessible depuis le radar via le lien **"Equipe"** dans le header de navigation.
|
||||
|
||||
## Architecture Technique
|
||||
|
||||
La page utilise une architecture basee sur l'injection de contenu cote client :
|
||||
|
||||
- **Script principal** : `public/team-block-script.js` charge par Next.js via `_document.tsx`
|
||||
- **Remplacement DOM** : Le script detecte la route `/team` et remplace le contenu du body
|
||||
- **Chargement dynamique** : Cytoscape.js et ECharts sont charges depuis CDN
|
||||
- **Donnees JSON** : `public/team-visualization-data.json` genere par script Node.js
|
||||
- **Navigation** : Lien integre dans `Navigation.tsx` (modifie par script Python lors du build)
|
||||
|
||||
Cette approche evite les conflits SSR tout en permettant des visualisations interactives riches.
|
||||
|
||||
### Fichiers impliques
|
||||
|
||||
- **Script principal** : `public/team-block-script.js` (injection du contenu et visualisations)
|
||||
- **Page Next.js** : `radar-app/src/pages/team.tsx` (page vide, le script remplace le contenu)
|
||||
- **Donnees JSON** : `public/team-visualization-data.json` (genere par `scripts/generate-team-visualization-data.js`)
|
||||
- **Navigation** : `radar-app/src/components/Navigation/Navigation.tsx` (modifiee par script Python)
|
||||
- **Document modifie** : `radar-app/src/pages/_document.tsx` (modifie pour charger le script)
|
||||
|
||||
## Acces
|
||||
|
||||
- **URL** : `/team` ou `/team/`
|
||||
- **Lien de navigation** : Disponible dans le header du radar (bouton "Equipe")
|
||||
- **Authentification** : Meme protection que le radar (mot de passe `laplank-radar`)
|
||||
|
||||
## Fonctionnalites
|
||||
|
||||
La page propose trois visualisations complementaires accessibles via des onglets :
|
||||
|
||||
### 1. Graphe Reseau
|
||||
|
||||
**Objectif** : Visualiser les connexions entre technologies et membres de l'equipe.
|
||||
|
||||
**Fonctionnalites** :
|
||||
- **Noeuds technologies** :
|
||||
- Couleur selon le ring (Adopt=vert, Trial=bleu, Assess=orange, Hold=rouge)
|
||||
- Taille proportionnelle au nombre de personnes maitrisant la technologie
|
||||
- Label avec le nom de la technologie
|
||||
- **Noeuds membres** :
|
||||
- Couleur verte
|
||||
- Taille proportionnelle a la disponibilite
|
||||
- Label avec le nom du membre
|
||||
- **Liens** :
|
||||
- Connectent les technologies aux membres qui les maitrisent
|
||||
- Epaisseur selon le niveau de competence
|
||||
|
||||
**Utilisation** :
|
||||
- Zoom : Molette de la souris ou pincement sur mobile
|
||||
- Pan : Clic gauche + glisser
|
||||
- Animation : Le graphe s'organise automatiquement au chargement
|
||||
|
||||
**Technologie** : Cytoscape.js avec le layout CoSE (integre)
|
||||
|
||||
### 2. Matrice de Congestion
|
||||
|
||||
**Objectif** : Identifier les technologies avec faible couverture d'equipe (congestions).
|
||||
|
||||
**Fonctionnalites** :
|
||||
- **Axe X** : Membres de l'equipe
|
||||
- **Axe Y** : Technologies (uniquement les technologies "adopt")
|
||||
- **Points verts** : Indiquent qu'un membre maitrise une technologie
|
||||
- **Taille des points** : Proportionnelle a la disponibilite du membre
|
||||
|
||||
**Utilisation** :
|
||||
- Survoler un point pour voir les details (membre, technologie, disponibilite)
|
||||
- Identifier visuellement les technologies avec peu de personnes (peu de points sur une ligne)
|
||||
|
||||
**Note** : Seules les technologies avec `ring: adopt` sont affichees dans la matrice, car ce sont les technologies fondamentales en production.
|
||||
|
||||
**Technologie** : ECharts (scatter plot)
|
||||
|
||||
### 3. Equipe de Genese MVP
|
||||
|
||||
**Objectif** : Composer automatiquement une equipe minimale pour un MVP en 2 mois avec mobilisation quotidienne.
|
||||
|
||||
**Fonctionnalites** :
|
||||
- **Selection automatique** : Algorithme qui selectionne les membres selon :
|
||||
- Disponibilite >= 50%
|
||||
- Couverture maximale des technologies critiques (technologies avec `ring: adopt`)
|
||||
- Equilibre des competences
|
||||
- **Statistiques** :
|
||||
- Nombre de membres selectionnes
|
||||
- Capacite totale (somme des disponibilites)
|
||||
- Disponibilite moyenne
|
||||
- Technologies couvertes / total
|
||||
- **Cartes membres** :
|
||||
- Nom et role
|
||||
- Niveau de seniorite
|
||||
- Disponibilite
|
||||
- Technologies maitrisees
|
||||
- **Alertes** :
|
||||
- Technologies critiques non couvertes (en rouge)
|
||||
- Impact business et skill gap pour chaque technologie manquante
|
||||
|
||||
**Technologie** : HTML/CSS genere dynamiquement par JavaScript
|
||||
|
||||
## Donnees
|
||||
|
||||
### Sources de donnees
|
||||
|
||||
1. **Profils equipe** : `docs/data/team/*.md` (fichiers Markdown avec metadonnees YAML)
|
||||
2. **Technologies** : `radar-business/2025-01-15/*.md` (blips avec metadonnees)
|
||||
|
||||
### Generation des donnees
|
||||
|
||||
Le script `scripts/generate-team-visualization-data.js` :
|
||||
|
||||
1. **Lit les profils equipe** depuis `docs/data/team/*.md`
|
||||
2. **Lit les technologies** depuis `radar-business/2025-01-15/*.md`
|
||||
3. **Genere** `public/team-visualization-data.json` avec :
|
||||
- `network` : Donnees reseau (nodes/edges) pour Cytoscape.js
|
||||
- `congestionMatrix` : Matrice de congestion pour ECharts
|
||||
- `genesisTeam` : Equipe de genese MVP avec statistiques
|
||||
- `technologies` : Liste des technologies avec metadonnees
|
||||
- `members` : Liste des membres avec competences
|
||||
- `generatedAt` : Date de generation
|
||||
|
||||
### Executer la generation
|
||||
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
**Important** : Regenerer les donnees apres chaque modification des profils equipe ou des technologies.
|
||||
|
||||
## Structure des profils equipe
|
||||
|
||||
Les profils sont stockes dans `docs/data/team/*.md` avec le format suivant :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: "pseudo"
|
||||
fullName: "Nom complet"
|
||||
role: "Role"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Python"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
- "Pedagogie"
|
||||
projects:
|
||||
- "Projet1"
|
||||
- "Projet2"
|
||||
---
|
||||
|
||||
Description du membre de l'equipe.
|
||||
```
|
||||
|
||||
### Metadonnees
|
||||
|
||||
- **name** : Pseudo unique (utilise comme identifiant)
|
||||
- **fullName** : Nom complet
|
||||
- **role** : Role dans l'equipe
|
||||
- **availability** : Disponibilite en pourcentage (0-100)
|
||||
- **seniorityLevel** : Niveau de seniorite (junior, intermediate, senior, expert)
|
||||
- **yearsExperience** : Annees d'experience totales
|
||||
- **joinDate** : Date d'arrivee (format YYYY-MM)
|
||||
- **interests** : Centres d'interet
|
||||
- **skills** : Liste des competences techniques avec niveau, annees et derniere utilisation
|
||||
- **softSkills** : Competences comportementales
|
||||
- **projects** : Projets sur lesquels le membre a travaille
|
||||
|
||||
## Processus de build
|
||||
|
||||
### Dans le Dockerfile
|
||||
|
||||
1. **Copie des fichiers publics** : `public/team-block-script.js` et `public/team-visualization-data.json` vers `radar-app/public/`
|
||||
2. **Creation de la page Next.js** : Genere `radar-app/src/pages/team.tsx` (page vide)
|
||||
3. **Modification de _document.tsx** : Ajoute le chargement de `team-block-script.js` avec `strategy="beforeInteractive"`
|
||||
4. **Modification de Navigation** : Ajoute le lien "Equipe" dans `Navigation.tsx` via script Python
|
||||
5. **Build Next.js** : Genere les fichiers statiques dans `out/`
|
||||
6. **Copie finale** : S'assure que les fichiers team sont dans `out/`
|
||||
|
||||
### Script Python pour Navigation.tsx
|
||||
|
||||
Le script `docker/add_team_link.py` :
|
||||
|
||||
1. **Supprime tous les liens Equipe existants** : Evite les doublons
|
||||
2. **Ajoute un seul lien Equipe** : Apres le lien "Vue d'ensemble"
|
||||
3. **Verifie le resultat** : S'assure qu'il n'y a qu'un seul lien
|
||||
|
||||
### Script Python pour _document.tsx
|
||||
|
||||
Le script `docker/patch_document.py` :
|
||||
|
||||
1. **Ajoute l'import de Script** : Si pas deja present
|
||||
2. **Ajoute le chargement du script** : `team-block-script.js` avec `strategy="beforeInteractive"`
|
||||
|
||||
## Technologies utilisees
|
||||
|
||||
### Bibliotheques JavaScript (chargees depuis CDN)
|
||||
|
||||
- **Cytoscape.js** : Graphique reseau interactif (v3.26.0)
|
||||
- **ECharts** : Matrice de congestion scatter plot (v5.4.3)
|
||||
|
||||
### Layout du graphe
|
||||
|
||||
Le graphe reseau utilise le layout **CoSE** (Compound Spring Embedder) integre a Cytoscape.js. Ce layout :
|
||||
- Organise les noeuds avec un algorithme force-directed
|
||||
- Prend en compte les labels pour eviter les chevauchements
|
||||
- S'anime lors du chargement initial
|
||||
|
||||
### Chargement des donnees
|
||||
|
||||
Les donnees sont chargees depuis `/team-visualization-data.json` via `fetch()` au chargement de la page.
|
||||
|
||||
## Fonctionnement du script
|
||||
|
||||
Le script `team-block-script.js` fonctionne ainsi :
|
||||
|
||||
1. **Detection de la route** : Verifie si le chemin commence par `/team`
|
||||
2. **Remplacement du DOM** : Injecte le HTML de la page (header, onglets, containers)
|
||||
3. **Injection du CSS** : Ajoute les styles pour la page
|
||||
4. **Chargement des bibliotheques** : Charge Cytoscape et ECharts depuis CDN
|
||||
5. **Chargement des donnees** : Fetch `/team-visualization-data.json`
|
||||
6. **Initialisation des visualisations** : Cree le graphe, la matrice et l'equipe de genese
|
||||
7. **Gestion des onglets** : Permet de basculer entre les trois visualisations
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Visualiser les competences
|
||||
|
||||
1. Ouvrir l'onglet **"Graphe Reseau"**
|
||||
2. Explorer les connexions entre technologies et membres
|
||||
3. Identifier les technologies avec beaucoup de personnes (gros noeuds)
|
||||
4. Identifier les technologies avec peu de personnes (petits noeuds = congestions)
|
||||
|
||||
### Identifier les congestions
|
||||
|
||||
1. Ouvrir l'onglet **"Matrice Congestion"**
|
||||
2. Reperer les lignes (technologies) avec peu de points verts
|
||||
3. Ces technologies ont une faible couverture d'equipe
|
||||
4. Actions recommandees :
|
||||
- Former l'equipe
|
||||
- Recruter
|
||||
- Documenter
|
||||
|
||||
### Composer une Equipe pour un Projet
|
||||
|
||||
1. Ouvrir l'onglet **"Equipe Genese MVP"**
|
||||
2. L'equipe est automatiquement selectionnee selon :
|
||||
- Disponibilite >= 50%
|
||||
- Couverture maximale des technologies
|
||||
3. Consulter les statistiques et les cartes membres
|
||||
4. Verifier les technologies non couvertes (en rouge)
|
||||
|
||||
## Personnalisation
|
||||
|
||||
### Modifier le seuil de disponibilite
|
||||
|
||||
Pour l'equipe de genese MVP, le seuil est fixe a **50%**. Pour le modifier :
|
||||
|
||||
1. Ouvrir `scripts/generate-team-visualization-data.js`
|
||||
2. Modifier le filtre `m.availability >= 50` dans les fonctions `generateCongestionMatrix()` et `generateGenesisTeam()`
|
||||
3. Regenerer les donnees : `node scripts/generate-team-visualization-data.js`
|
||||
|
||||
**Note** : Les technologies critiques utilisees pour la selection sont celles avec `ring: adopt`. Pour modifier ce critere, changer le filtre `t.ring === 'adopt'` dans `generateGenesisTeam()`.
|
||||
|
||||
### Modifier le layout du graphe
|
||||
|
||||
Le layout est configure dans `public/team-block-script.js` dans la fonction `initNetwork()` :
|
||||
|
||||
```javascript
|
||||
layout: {
|
||||
name: 'cose',
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
idealEdgeLength: 100,
|
||||
nodeRepulsion: 4500,
|
||||
gravity: 0.25,
|
||||
numIter: 1000,
|
||||
animate: true,
|
||||
animationDuration: 800
|
||||
}
|
||||
```
|
||||
|
||||
Parametres disponibles :
|
||||
- `nodeRepulsion` : Force de repulsion entre noeuds (plus eleve = plus espaces)
|
||||
- `gravity` : Force de gravite vers le centre
|
||||
- `numIter` : Nombre d'iterations de l'algorithme
|
||||
- `animationDuration` : Duree de l'animation en ms
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Mettre a jour les profils equipe
|
||||
|
||||
1. Modifier les fichiers dans `docs/data/team/*.md`
|
||||
2. Regenerer les donnees : `node scripts/generate-team-visualization-data.js`
|
||||
3. Commiter les deux fichiers (profils + donnees JSON)
|
||||
4. Rebuild Docker pour deployer
|
||||
|
||||
### Ajouter un nouveau membre
|
||||
|
||||
1. Creer un fichier `docs/data/team/pseudo.md`
|
||||
2. Remplir toutes les metadonnees (voir format ci-dessus)
|
||||
3. Regenerer les donnees
|
||||
4. Verifier que le membre apparait dans les visualisations
|
||||
|
||||
### Mettre a jour les competences
|
||||
|
||||
1. Modifier la section `skills` dans le profil equipe
|
||||
2. Regenerer les donnees
|
||||
3. Verifier que les connexions sont mises a jour dans le graphe
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Le lien "Equipe" n'apparait pas dans le header
|
||||
|
||||
**Causes possibles** :
|
||||
- Le script Python n'a pas ete execute
|
||||
- Le fichier Navigation.tsx n'a pas ete trouve
|
||||
- Erreur dans le script Python
|
||||
|
||||
**Solutions** :
|
||||
1. Verifier les logs Docker lors du build
|
||||
2. Verifier que le fichier `radar-app/src/components/Navigation/Navigation.tsx` existe
|
||||
3. Rebuild avec `--no-cache` pour forcer l'execution du script
|
||||
|
||||
### La page `/team` affiche le radar au lieu des visualisations
|
||||
|
||||
**Causes possibles** :
|
||||
- Le script `team-block-script.js` n'est pas charge
|
||||
- Le script n'est pas modifie dans `_document.tsx`
|
||||
|
||||
**Solutions** :
|
||||
1. Verifier la console du navigateur (F12) pour les erreurs
|
||||
2. Verifier que le script `patch_document.py` a ete execute lors du build
|
||||
3. Rebuild avec `--no-cache`
|
||||
|
||||
### Les visualisations sont vides ou affichent une erreur
|
||||
|
||||
**Causes possibles** :
|
||||
- Le fichier `team-visualization-data.json` n'a pas ete genere
|
||||
- Le fichier n'est pas accessible depuis le navigateur
|
||||
- Erreur dans les donnees
|
||||
|
||||
**Solutions** :
|
||||
1. Generer les donnees : `node scripts/generate-team-visualization-data.js`
|
||||
2. Verifier que le fichier existe dans `public/team-visualization-data.json`
|
||||
3. Verifier la console du navigateur pour les erreurs JavaScript
|
||||
4. Tester l'acces direct : `http://localhost:3006/team-visualization-data.json`
|
||||
|
||||
### Le graphe ne s'affiche pas
|
||||
|
||||
**Causes possibles** :
|
||||
- Cytoscape.js n'a pas pu etre charge depuis CDN
|
||||
- Erreur dans la configuration du layout
|
||||
|
||||
**Solutions** :
|
||||
1. Verifier la connexion internet (CDN)
|
||||
2. Verifier la console pour les erreurs de chargement
|
||||
3. Verifier que le layout `cose` est bien utilise (pas `cose-bilkent`)
|
||||
|
||||
### Les donnees ne sont pas a jour
|
||||
|
||||
**Solution** : Regenerer les donnees apres chaque modification :
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
|
||||
Puis rebuild Docker :
|
||||
```bash
|
||||
docker compose -f docker-compose.business.yml build --no-cache
|
||||
docker compose -f docker-compose.business.yml up -d
|
||||
```
|
||||
|
||||
## Fichiers associes
|
||||
|
||||
- **Script principal** : `public/team-block-script.js` (injection et visualisations)
|
||||
- **Page Next.js** : `docker/team-page.tsx` (page vide copiee vers `radar-app/src/pages/team.tsx`)
|
||||
- **Donnees JSON** : `public/team-visualization-data.json` (genere)
|
||||
- **Script de generation** : `scripts/generate-team-visualization-data.js`
|
||||
- **Profils equipe** : `docs/data/team/*.md` (fichiers Markdown avec metadonnees YAML)
|
||||
- **Technologies** : `radar-business/2025-01-15/*.md` (blips avec metadonnees)
|
||||
- **Script Navigation** : `docker/add_team_link.py` (ajoute le lien dans Navigation.tsx)
|
||||
- **Script Document** : `docker/patch_document.py` (ajoute le chargement du script)
|
||||
|
||||
## Ressources
|
||||
|
||||
- [Guide de developpement](./developpement.md)
|
||||
- [Guide de deploiement](./deploiement.md)
|
||||
- [Guide de depannage](./troubleshooting.md)
|
||||
- Documentation Cytoscape.js : https://js.cytoscape.org/
|
||||
- Documentation ECharts : https://echarts.apache.org/
|
||||
278
docs/app/guide-radar-business.md
Normal file
278
docs/app/guide-radar-business.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# Guide d'Utilisation du Radar Technologique Laplank
|
||||
|
||||
## Introduction
|
||||
|
||||
Le Radar Technologique Laplank est un tech radar classique pour suivre l'évolution des technologies de l'écosystème Laplank/Duniter/Ğ1. Il permet de suivre l'adoption des technologies au fil du temps avec un historique par release.
|
||||
|
||||
## Accès
|
||||
|
||||
Le Radar Technologique Laplank est accessible sur le **port 3006** et est protégé par un **mot de passe** : `laplank-radar`
|
||||
|
||||
L'authentification est gérée côté client via `localStorage` (session valide jusqu'à fermeture du navigateur).
|
||||
|
||||
## Structure du Radar
|
||||
|
||||
### Quadrants
|
||||
|
||||
Le radar est organisé en 4 quadrants business :
|
||||
|
||||
1. **Technologies Différenciantes** : Créent un avantage concurrentiel
|
||||
2. **Technologies de Commodité** : Nécessaires mais non différenciantes
|
||||
3. **Technologies à Risque** : Obsolètes, coûteuses, à migrer
|
||||
4. **Technologies Émergentes** : Opportunités futures
|
||||
|
||||
### Anneaux (Rings)
|
||||
|
||||
Chaque technologie est classée dans un des 4 anneaux classiques :
|
||||
|
||||
1. **Adopt** : Technologies recommandées et utilisées avec succès en production. Stables, éprouvées, peuvent être adoptées en toute confiance pour de nouveaux projets.
|
||||
2. **Trial** : Technologies à essayer. Prometteuses et testées avec succès dans certains contextes. À considérer pour de nouveaux projets.
|
||||
3. **Assess** : Technologies à évaluer. Prometteuses mais nécessitent une évaluation approfondie avant adoption. À surveiller et tester.
|
||||
4. **Hold** : Technologies à éviter ou à remplacer. Présentent des risques, sont obsolètes ou ne sont plus recommandées. À éviter pour de nouveaux projets.
|
||||
|
||||
**Important** : Tous les blips doivent utiliser ces rings standards (adopt, trial, assess, hold). Les anciens rings (core, strategic, support, legacy) ne sont plus utilisés.
|
||||
|
||||
## Historique des Technologies
|
||||
|
||||
Le Radar Technologique Laplank suit l'évolution des technologies au fil du temps avec un système d'historique par release.
|
||||
|
||||
### Structure par Release
|
||||
|
||||
Les technologies sont organisées par date de release dans des dossiers :
|
||||
- `radar-business/2025-01-15/` : Release de janvier 2025
|
||||
- `radar-business/2025-04-15/` : Release d'avril 2025 (exemple)
|
||||
- etc.
|
||||
|
||||
### Suivi de l'Évolution
|
||||
|
||||
Chaque technologie peut évoluer entre les releases :
|
||||
- **Nouveau** : Technologie ajoutée dans cette release
|
||||
- **Modifié** : Technologie déplacée (ring ou quadrant) ou description mise à jour
|
||||
- **Inchangé** : Technologie stable, pas de changement
|
||||
|
||||
### Créer une Nouvelle Release
|
||||
|
||||
Pour créer une nouvelle release :
|
||||
|
||||
1. Créer un nouveau dossier avec la date au format `YYYY-MM-DD` :
|
||||
```bash
|
||||
mkdir radar-business/2025-04-15
|
||||
```
|
||||
|
||||
2. Copier les blips pertinents depuis la release précédente
|
||||
|
||||
3. Mettre à jour les blips existants si nécessaire (changement de ring, quadrant, description)
|
||||
|
||||
4. **Migrer les rings si nécessaire** : S'assurer que tous les blips utilisent les rings standards (adopt, trial, assess, hold)
|
||||
|
||||
5. Ajouter les nouveaux blips pour les technologies nouvellement évaluées
|
||||
|
||||
## Métadonnées Business
|
||||
|
||||
Chaque technologie (blip) contient des métadonnées business :
|
||||
|
||||
### Métadonnées Standard
|
||||
|
||||
- **title** : Nom de la technologie
|
||||
- **ring** : Anneau (adopt, trial, assess, hold) - **IMPORTANT** : Utiliser uniquement ces rings standards
|
||||
- **quadrant** : Quadrant business
|
||||
- **tags** : Tags pour le filtrage
|
||||
|
||||
### Métadonnées Business
|
||||
|
||||
- **businessImpact** : Impact sur le business (high, medium, low)
|
||||
- **costToReplace** : Coût estimé de remplacement en euros
|
||||
- **revenueImpact** : Impact sur les revenus (direct, indirect, none)
|
||||
- **riskLevel** : Niveau de risque (high, medium, low)
|
||||
- **maintenanceCost** : Coût annuel de maintenance en euros
|
||||
- **differentiation** : Capacité de différenciation (high, medium, low)
|
||||
|
||||
### Métadonnées Compétences
|
||||
|
||||
- **competencyLevel** : Niveau moyen de compétence (expert, intermediate, beginner)
|
||||
- **teamCoverage** : Nombre de personnes maîtrisant la technologie
|
||||
- **skillGap** : Risque de compétence manquante (high, medium, low)
|
||||
|
||||
## Pages de Stratégie
|
||||
|
||||
Le Radar Technologique Laplank inclut trois pages de stratégie accessibles depuis le header :
|
||||
|
||||
1. **Stratégie Technique** : Vision et roadmap technique pour l'évolution du stack
|
||||
2. **Business** : Analyse stratégique business autour de la dataviz et des flux économiques
|
||||
3. **DataViz Expert** : Opportunités supplémentaires en dataviz (Smart Cities, Green Tech, KM, Cybersecurity)
|
||||
|
||||
Ces pages sont générées dynamiquement via `public/strategie-script.js` qui convertit le contenu Markdown en HTML.
|
||||
|
||||
**Note** : Les fonctions d'ajout de liens dans le header ont été désactivées pour éviter les doublons. Tous les liens sont maintenant gérés par `Navigation.tsx`.
|
||||
|
||||
### Contenu des Pages
|
||||
|
||||
Le contenu des pages de stratégie est intégré directement dans `public/strategie-script.js` :
|
||||
- `docs/data/strategie-evolution-technique.md` : Stratégie d'évolution technique
|
||||
- `docs/data/strategie-business.md` : Analyse business et opportunités
|
||||
- `docs/data/opportunites-dataviz.md` et `docs/data/opportunites-dataviz-details.md` : Opportunités DataViz
|
||||
|
||||
Pour modifier le contenu, éditer directement `public/strategie-script.js` (sections `markdownContent`) ou les fichiers sources dans `docs/data/`.
|
||||
|
||||
## Navigation
|
||||
|
||||
Le header de navigation contient les liens suivants :
|
||||
- **Comment utiliser le Radar Technologique ?** : Page d'aide
|
||||
- **Vue d'ensemble des technologies** : Vue d'ensemble
|
||||
- **👥 Équipe** : Page de visualisation équipe/technologies
|
||||
|
||||
**Important** : Tous les liens sont gérés par `Navigation.tsx`. Les scripts JavaScript qui ajoutaient des liens ont été désactivés pour éviter les doublons.
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Ajouter une Nouvelle Technologie
|
||||
|
||||
1. Créer un fichier Markdown dans `radar-business/2025-01-15/`
|
||||
2. Utiliser le format défini dans `radar-business/FORMAT-BLIP.md`
|
||||
3. **Utiliser les rings standards** : adopt, trial, assess, hold
|
||||
4. Remplir toutes les métadonnées
|
||||
5. Ajouter la description et les sections recommandées
|
||||
|
||||
### Modifier une Technologie Existante
|
||||
|
||||
1. Ouvrir le fichier Markdown correspondant
|
||||
2. Modifier les métadonnées ou le contenu
|
||||
3. **Vérifier que le ring est standard** : adopt, trial, assess, hold
|
||||
4. Mettre à jour la date si changement significatif
|
||||
|
||||
### Analyser le Radar
|
||||
|
||||
1. Exécuter le script d'analyse :
|
||||
```bash
|
||||
node scripts/analyze-business-metrics.js
|
||||
```
|
||||
2. Consulter le rapport généré dans `docs/data/analyse-strategique.md`
|
||||
|
||||
### Générer les Blips
|
||||
|
||||
Pour régénérer les blips depuis `docs/data/technologies-duniter.md` :
|
||||
|
||||
```bash
|
||||
node scripts/extract-technologies.js
|
||||
```
|
||||
|
||||
## Migration des Rings
|
||||
|
||||
Si vous avez des blips avec les anciens rings, utilisez ce mapping :
|
||||
|
||||
- **core** → **adopt** : Technologies fondamentales en production
|
||||
- **strategic** → **assess** : Technologies prometteuses à évaluer
|
||||
- **support** → **adopt** : Technologies utilisées en production
|
||||
- **trial** → **trial** : Déjà correct
|
||||
- **legacy** → **hold** : Technologies obsolètes à remplacer
|
||||
|
||||
Le script `scripts/migrate-rings.sh` peut être utilisé pour automatiser cette migration.
|
||||
|
||||
## Interprétation des Résultats
|
||||
|
||||
### Technologies Critiques
|
||||
|
||||
Les technologies en ring "adopt" avec businessImpact "high" sont critiques. Elles nécessitent :
|
||||
- Maintenance proactive
|
||||
- Formation continue
|
||||
- Documentation exhaustive
|
||||
- Plans de continuité
|
||||
|
||||
### Technologies à Risque
|
||||
|
||||
Les technologies avec riskLevel "high" ou skillGap "high" présentent des risques. Actions recommandées :
|
||||
- Formation ou recrutement
|
||||
- Documentation
|
||||
- Plan de migration si nécessaire
|
||||
|
||||
### Opportunités d'Innovation
|
||||
|
||||
Les technologies émergentes avec differentiation "high" sont des opportunités. Actions :
|
||||
- POC (Proof of Concept)
|
||||
- Évaluation de l'impact
|
||||
- Adoption progressive
|
||||
|
||||
### Optimisation des Coûts
|
||||
|
||||
Les technologies de commodité avec maintenanceCost élevé peuvent être optimisées :
|
||||
- Standardisation
|
||||
- Automatisation
|
||||
- Réduction des coûts
|
||||
|
||||
## Méthodologie d'Analyse
|
||||
|
||||
### 1. Collecte des Données
|
||||
|
||||
- Inventorier toutes les technologies
|
||||
- Collecter les métadonnées business
|
||||
- Analyser les compétences de l'équipe
|
||||
|
||||
### 2. Classification
|
||||
|
||||
- Classer par quadrant business
|
||||
- Classer par ring (adopt, trial, assess, hold) - **utiliser uniquement ces rings**
|
||||
- Évaluer les métadonnées
|
||||
|
||||
### 3. Analyse
|
||||
|
||||
- Identifier les patterns
|
||||
- Calculer les métriques
|
||||
- Identifier les risques et opportunités
|
||||
|
||||
### 4. Recommandations
|
||||
|
||||
- Prioriser les actions
|
||||
- Définir la roadmap
|
||||
- Planifier les investissements
|
||||
|
||||
## Templates
|
||||
|
||||
### Template de Blip
|
||||
|
||||
Voir `radar-business/FORMAT-BLIP.md` pour le template complet avec toutes les métadonnées.
|
||||
|
||||
### Template d'Analyse
|
||||
|
||||
Le script `analyze-business-metrics.js` génère automatiquement un rapport d'analyse.
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Mise à Jour Régulière
|
||||
|
||||
- Mettre à jour les métadonnées trimestriellement
|
||||
- Réviser les classifications annuellement
|
||||
- Mettre à jour les coûts et risques
|
||||
- **Vérifier que tous les blips utilisent les rings standards**
|
||||
|
||||
### Révision Stratégique
|
||||
|
||||
- Révision annuelle de la stratégie
|
||||
- Ajustement des priorités
|
||||
- Mise à jour de la roadmap
|
||||
|
||||
## Déploiement
|
||||
|
||||
Le Radar Technologique Laplank est déployé via Docker et Portainer :
|
||||
|
||||
- **Dockerfile** : `Dockerfile.business`
|
||||
- **Docker Compose** : `docker-compose.business.yml`
|
||||
- **Port** : 3006 (mappé depuis le port 3000 du conteneur)
|
||||
- **Base path** : `/` (racine, pas de sous-chemin)
|
||||
|
||||
Voir [deploiement.md](./deploiement.md) pour les détails complets.
|
||||
|
||||
## Ressources
|
||||
|
||||
- **Format des blips** : `radar-business/FORMAT-BLIP.md`
|
||||
- **Configuration** : `radar-business/config-business.json`
|
||||
- **Script de stratégie** : `public/strategie-script.js`
|
||||
- **Analyse stratégique** : `docs/data/analyse-strategique.md`
|
||||
- **Stratégie d'évolution** : `docs/data/strategie-evolution-technique.md`
|
||||
- **Stratégie business** : `docs/data/strategie-business.md`
|
||||
- **Opportunités DataViz** : `docs/data/opportunites-dataviz.md` et `docs/data/opportunites-dataviz-details.md`
|
||||
- **Technologies Duniter** : `docs/data/technologies-duniter.md`
|
||||
- **Profils Team** : `docs/data/team/*.md` (fichiers individuels)
|
||||
|
||||
## Support
|
||||
|
||||
Pour toute question ou contribution, consulter la documentation ou contacter l'équipe technique.
|
||||
89
docs/app/migration-nextjs-16.md
Normal file
89
docs/app/migration-nextjs-16.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Migration vers Next.js 16.1.6
|
||||
|
||||
## Résumé des changements
|
||||
|
||||
Migration de Next.js de la version **15.2.4** vers **16.1.6** (dernière version stable).
|
||||
|
||||
## Modifications apportées
|
||||
|
||||
### Dépendances mises à jour
|
||||
|
||||
- **next** : `15.2.4` → `16.1.6`
|
||||
- **eslint-config-next** : `15.2.4` → `16.1.6`
|
||||
|
||||
### Dépendances conservées (compatibles)
|
||||
|
||||
- **react** : `^19` (déjà à jour, compatible avec Next.js 16)
|
||||
- **react-dom** : `^19` (déjà à jour, compatible avec Next.js 16)
|
||||
- **@types/react** : `^19` (compatible)
|
||||
- **@types/react-dom** : `^19` (compatible)
|
||||
- **typescript** : `^5` (compatible, minimum requis 5.1.0)
|
||||
|
||||
## Changements dans Next.js 16
|
||||
|
||||
### Exigences système
|
||||
|
||||
- **Node.js** : Minimum 20.9.0 (déjà utilisé dans Dockerfile avec Node 20)
|
||||
- **TypeScript** : Minimum 5.1.0 (déjà satisfait avec TypeScript 5)
|
||||
- **Browsers** : Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+
|
||||
|
||||
### Turbopack par défaut
|
||||
|
||||
- Turbopack est maintenant stable et utilisé par défaut pour `next dev` et `next build`
|
||||
- Plus besoin du flag `--turbopack` dans les scripts
|
||||
- Si vous utilisez une configuration webpack personnalisée, les builds échoueront par défaut
|
||||
- Solution : migrer vers Turbopack ou utiliser `next build --webpack` pour désactiver
|
||||
|
||||
### Configuration actuelle
|
||||
|
||||
Le fichier `radar-app/next.config.js` actuel est compatible avec Next.js 16 :
|
||||
|
||||
```javascript
|
||||
const nextConfig = {
|
||||
basePath,
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Note** : L'option `experimental.scrollRestoration` pourrait être dépréciée dans Next.js 16, mais ne devrait pas causer d'erreur.
|
||||
|
||||
## Tests à effectuer
|
||||
|
||||
1. ✅ Build Docker : Vérifier que le build fonctionne avec la nouvelle version
|
||||
2. ✅ Serveur de développement : Tester `npm run serve-dev`
|
||||
3. ✅ Build de production : Tester `npm run build`
|
||||
4. ✅ Page d'accueil : Vérifier le rendu
|
||||
5. ✅ Page team : Vérifier les visualisations
|
||||
6. ✅ Navigation : Vérifier tous les liens
|
||||
|
||||
## Migration automatique (optionnel)
|
||||
|
||||
Si des problèmes surviennent, vous pouvez utiliser le codemod officiel :
|
||||
|
||||
```bash
|
||||
cd radar-app
|
||||
npx @next/codemod@canary upgrade latest
|
||||
```
|
||||
|
||||
Ce codemod gère automatiquement :
|
||||
- Suppression de `experimental_ppr` Route Segment Config
|
||||
- Suppression du préfixe `unstable_` des APIs stabilisées
|
||||
- Migration de la convention `middleware` dépréciée vers `proxy`
|
||||
- Migration de `next lint` vers ESLint CLI
|
||||
- Mise à jour de `next.config.js` pour la nouvelle configuration Turbopack
|
||||
|
||||
## Notes importantes
|
||||
|
||||
- Le projet utilise déjà React 19, qui est compatible avec Next.js 16
|
||||
- Le Dockerfile utilise Node.js 20, ce qui satisfait l'exigence minimale
|
||||
- Aucun changement de code source n'est nécessaire pour cette migration
|
||||
- Les dépendances optionnelles (cytoscape, echarts-for-react) restent inchangées
|
||||
|
||||
## Date de migration
|
||||
|
||||
Migration effectuée le : 2026-02-25
|
||||
238
docs/app/plan-edition-cms.md
Normal file
238
docs/app/plan-edition-cms.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Plan d'implementation - Edition des profils via NetlifyCMS + Gitea
|
||||
|
||||
## Objectif
|
||||
|
||||
Permettre l'edition des profils de membres et des technologies directement depuis l'interface web, avec regeneration automatique des visualisations.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
+------------------+ +------------------+ +------------------+
|
||||
| UTILISATEUR | | NetlifyCMS | | GITEA |
|
||||
| |---->| /admin |---->| git.open.us.org |
|
||||
| Clic "Editer" | | Formulaires | | OAuth + API |
|
||||
+------------------+ +------------------+ +------------------+
|
||||
|
|
||||
v (webhook)
|
||||
+------------------+
|
||||
| CI/CD |
|
||||
| - npm build |
|
||||
| - regenere JSON |
|
||||
| - deploie |
|
||||
+------------------+
|
||||
```
|
||||
|
||||
## Etapes d'implementation
|
||||
|
||||
### Etape 1 : Configurer OAuth sur Gitea (15 min)
|
||||
|
||||
1. Aller dans **Settings > Applications > OAuth2 Applications**
|
||||
2. Creer une nouvelle application :
|
||||
- **Name** : `TechRadar CMS`
|
||||
- **Redirect URI** : `https://votre-domaine.com/admin/`
|
||||
3. Noter le **Client ID** et **Client Secret**
|
||||
|
||||
### Etape 2 : Creer les fichiers NetlifyCMS (1h)
|
||||
|
||||
#### Fichier : `public/admin/index.html`
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>TechRadar Admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### Fichier : `public/admin/config.yml`
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
name: gitea
|
||||
repo: AJR/TechradarDev
|
||||
branch: main
|
||||
api_root: https://git.open.us.org/api/v1
|
||||
base_url: https://git.open.us.org
|
||||
auth_endpoint: /login/oauth/authorize
|
||||
auth_type: implicit
|
||||
|
||||
media_folder: "public/images/team"
|
||||
public_folder: "/images/team"
|
||||
|
||||
collections:
|
||||
# Collection : Membres de l'equipe
|
||||
- name: "team"
|
||||
label: "Membres de l'equipe"
|
||||
folder: "docs/data/team"
|
||||
create: true
|
||||
delete: true
|
||||
extension: "md"
|
||||
format: "frontmatter"
|
||||
slug: "{{fields.name}}"
|
||||
fields:
|
||||
- { label: "Pseudo", name: "name", widget: "string", required: true }
|
||||
- { label: "Nom complet", name: "fullName", widget: "string" }
|
||||
- { label: "Role", name: "role", widget: "string" }
|
||||
- { label: "Disponibilite (%)", name: "availability", widget: "number", min: 0, max: 100, default: 50 }
|
||||
- { label: "Niveau de seniorite", name: "seniorityLevel", widget: "select",
|
||||
options: ["beginner", "intermediate", "senior", "expert"] }
|
||||
- { label: "Annees d'experience", name: "yearsExperience", widget: "number", min: 0 }
|
||||
- { label: "Date d'arrivee", name: "joinDate", widget: "string", hint: "Format: YYYY-MM" }
|
||||
- label: "Centres d'interet"
|
||||
name: "interests"
|
||||
widget: "list"
|
||||
field: { label: "Interet", name: "interest", widget: "string" }
|
||||
- label: "Competences"
|
||||
name: "skills"
|
||||
widget: "list"
|
||||
fields:
|
||||
- { label: "Nom", name: "name", widget: "string" }
|
||||
- { label: "Niveau", name: "level", widget: "select",
|
||||
options: ["beginner", "intermediate", "expert"] }
|
||||
- { label: "Annees", name: "years", widget: "number", min: 0 }
|
||||
- { label: "Derniere utilisation", name: "lastUsed", widget: "string" }
|
||||
- label: "Soft Skills"
|
||||
name: "softSkills"
|
||||
widget: "list"
|
||||
field: { label: "Skill", name: "skill", widget: "string" }
|
||||
- label: "Projets"
|
||||
name: "projects"
|
||||
widget: "list"
|
||||
field: { label: "Projet", name: "project", widget: "string" }
|
||||
- { label: "Bio", name: "body", widget: "markdown" }
|
||||
|
||||
# Collection : Technologies (blips radar)
|
||||
- name: "technologies"
|
||||
label: "Technologies"
|
||||
folder: "radar-business/2025-01-15"
|
||||
create: true
|
||||
extension: "md"
|
||||
format: "frontmatter"
|
||||
fields:
|
||||
- { label: "Titre", name: "title", widget: "string" }
|
||||
- { label: "Ring", name: "ring", widget: "select",
|
||||
options: ["adopt", "trial", "assess", "hold"] }
|
||||
- { label: "Quadrant", name: "quadrant", widget: "select",
|
||||
options:
|
||||
- { label: "Technologies de Commodite", value: "technologies-commodite" }
|
||||
- { label: "Technologies Differentiantes", value: "technologies-differentiantes" }
|
||||
- { label: "Technologies Emergentes", value: "technologies-emergentes" }
|
||||
- { label: "Technologies a Risque", value: "technologies-risque" }
|
||||
}
|
||||
- { label: "Impact Business", name: "businessImpact", widget: "select",
|
||||
options: ["low", "medium", "high"] }
|
||||
- { label: "Skill Gap", name: "skillGap", widget: "select",
|
||||
options: ["low", "medium", "high"] }
|
||||
- { label: "Description", name: "body", widget: "markdown" }
|
||||
```
|
||||
|
||||
### Etape 3 : Configurer le CI/CD (30 min)
|
||||
|
||||
Ajouter dans `.gitlab-ci.yml` :
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: node:20-alpine
|
||||
script:
|
||||
- npm install
|
||||
- node scripts/generate-team-visualization-data.js
|
||||
- npm run build
|
||||
artifacts:
|
||||
paths:
|
||||
- build/
|
||||
only:
|
||||
- main
|
||||
- dev-tech
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- docker compose -f docker-compose.business.yml build --no-cache
|
||||
- docker compose -f docker-compose.business.yml up -d
|
||||
only:
|
||||
- main
|
||||
```
|
||||
|
||||
### Etape 4 : Ajouter lien "Editer" dans la carte de profil (15 min)
|
||||
|
||||
Modifier `public/team-block-script.js` dans la fonction `showMemberProfile()` :
|
||||
|
||||
```javascript
|
||||
// Ajouter apres le bouton close :
|
||||
'<a href="/admin/#/collections/team/entries/' + memberId + '" ' +
|
||||
'target="_blank" class="edit-link" ' +
|
||||
'style="position:absolute;top:15px;right:50px;color:#4ade80;font-size:14px;text-decoration:none">' +
|
||||
'Editer</a>' +
|
||||
```
|
||||
|
||||
### Etape 5 : Gestion des CORS (si necessaire)
|
||||
|
||||
Si Gitea n'a pas les bons headers CORS, deployer un proxy OAuth :
|
||||
|
||||
```bash
|
||||
npm install netlify-cms-oauth-provider-node
|
||||
```
|
||||
|
||||
Ou configurer Nginx/Traefik pour ajouter les headers CORS.
|
||||
|
||||
## Workflow utilisateur
|
||||
|
||||
1. Utilisateur clique sur un pseudo dans le graphe/matrice/equipe
|
||||
2. Carte de profil s'affiche avec bouton "Editer"
|
||||
3. Clic sur "Editer" -> Redirige vers `/admin/#/collections/team/entries/pseudo`
|
||||
4. NetlifyCMS demande authentification Gitea (OAuth)
|
||||
5. Utilisateur modifie le formulaire (WYSIWYG)
|
||||
6. Clic "Publish" -> NetlifyCMS commit sur Gitea
|
||||
7. Webhook declenche le CI/CD
|
||||
8. CI regenere `team-visualization-data.json`
|
||||
9. Site redeploye avec les nouvelles donnees
|
||||
|
||||
## Estimation des temps
|
||||
|
||||
| Tache | Duree |
|
||||
|-------|-------|
|
||||
| Creer app OAuth Gitea | 15 min |
|
||||
| Creer `admin/index.html` et `config.yml` | 1h |
|
||||
| Configurer le backend Gitea dans NetlifyCMS | 1-2h |
|
||||
| Ajouter etape regeneration dans CI/CD | 30 min |
|
||||
| Ajouter lien "Editer" dans la carte profil | 15 min |
|
||||
| Tester le workflow complet | 1-2h |
|
||||
| **Total** | **4-6 heures** |
|
||||
|
||||
## Points d'attention
|
||||
|
||||
1. **CORS** : Gitea auto-heberge peut necessiter un proxy OAuth
|
||||
2. **Permissions** : Definir qui peut editer (tous les membres authentifies ou seulement certains)
|
||||
3. **Validation** : Les donnees doivent etre validees cote serveur
|
||||
4. **Conflits** : Gerer les editions simultanees (Git gere ca naturellement)
|
||||
|
||||
## Alternative simple : Lien direct vers l'editeur Git
|
||||
|
||||
En attendant l'implementation complete, on peut ajouter un simple lien :
|
||||
|
||||
```javascript
|
||||
'<a href="https://git.open.us.org/AJR/TechradarDev/_edit/main/docs/data/team/' + memberId + '.md" target="_blank">Editer sur Git</a>'
|
||||
```
|
||||
|
||||
## Ressources
|
||||
|
||||
- [NetlifyCMS Documentation](https://www.netlifycms.org/docs/)
|
||||
- [NetlifyCMS Gitea Backend](https://www.netlifycms.org/docs/gitea-backend/)
|
||||
- [Gitea OAuth2 Provider](https://docs.gitea.io/en-us/oauth2-provider/)
|
||||
|
||||
---
|
||||
|
||||
*Document cree le 2025-12-09*
|
||||
*Derniere mise a jour : 2025-12-09*
|
||||
|
||||
470
docs/app/roadmap-code-source.md
Normal file
470
docs/app/roadmap-code-source.md
Normal file
@@ -0,0 +1,470 @@
|
||||
# Roadmap basée sur le code source
|
||||
|
||||
**Date de création** : 2025-12-09
|
||||
**Basé sur** : Analyse du code source actuel (version 4.3.0)
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce document présente la roadmap de développement basée sur l'analyse du code source existant. Les priorités sont établies en fonction des fonctionnalités déjà implémentées, des scripts disponibles, et des opportunités d'amélioration identifiées dans le code.
|
||||
|
||||
---
|
||||
|
||||
## État actuel du système
|
||||
|
||||
### Architecture existante
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ APPLICATION STATIQUE │
|
||||
│ (Next.js export - 100% client-side) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ Radar │ │ Strategie │ │ Team │
|
||||
│ Tech │ │ (Markdown) │ │ Visualisation│
|
||||
└──────────┘ └──────────────┘ └──────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ Scripts │ │ Docker │ │ Data/JSON │
|
||||
│ Generation │ │ Deploy │ │ Static │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### Fonctionnalités implémentées
|
||||
|
||||
#### ✅ Core Application
|
||||
- **Radar technologique** : Visualisation interactive des technologies par quadrant/ring
|
||||
- **Pages stratégie** : 4 pages markdown intégrées (`strategie`, `business`, `dataviz`, `dataviz-details`)
|
||||
- **Page équipe** : Visualisation des compétences avec 3 vues (graphe réseau, matrice congestion, équipe genèse)
|
||||
- **Cartes de profil membres** : Modal cliquable depuis toutes les visualisations
|
||||
- **Authentification simple** : Protection par mot de passe (`laplank-radar`) pour pages stratégie
|
||||
|
||||
#### ✅ Scripts d'automatisation
|
||||
| Script | Fonction | Status |
|
||||
|--------|----------|--------|
|
||||
| `generate-team-visualization-data.js` | Génère `team-visualization-data.json` avec profils complets | ✅ Actif |
|
||||
| `analyze-business-metrics.js` | Analyse métriques business et génère rapport | ✅ Actif |
|
||||
| `extract-technologies.js` | Extrait technologies depuis markdown | ✅ Actif |
|
||||
| `verify-blips.js` | Vérifie la cohérence des blips | ✅ Actif |
|
||||
| `migrate-rings.sh` | Migration entre versions de rings | ✅ Actif |
|
||||
| `patch-navigation.sh` | Ajoute liens navigation | ✅ Actif |
|
||||
|
||||
#### ✅ Infrastructure
|
||||
- **Docker Compose** : Déploiement containerisé (business + local)
|
||||
- **Build automatisé** : Scripts npm pour build/serve
|
||||
- **CI/CD ready** : Structure prête pour intégration GitLab/GitHub
|
||||
|
||||
---
|
||||
|
||||
## Roadmap par priorité
|
||||
|
||||
### 🔴 Priorité 1 : Consolidation & Robustesse (Court terme - 1-3 mois)
|
||||
|
||||
#### 1.1 Améliorer la gestion des données (2 semaines)
|
||||
|
||||
**Problèmes identifiés dans le code :**
|
||||
- Parsing YAML manuel fragile dans `generate-team-visualization-data.js`
|
||||
- Pas de validation des schémas de données
|
||||
- Extraction de compétences basée sur regex (peut échouer)
|
||||
|
||||
**Actions :**
|
||||
```javascript
|
||||
// TODO: Remplacer parsing YAML manuel par librairie
|
||||
// Fichier: scripts/generate-team-visualization-data.js
|
||||
// Ligne ~12-16: extractYaml() fonction basique
|
||||
```
|
||||
|
||||
- [ ] Intégrer `js-yaml` pour parsing robuste
|
||||
- [ ] Ajouter validation JSON Schema pour profils membres
|
||||
- [ ] Créer script `validate-data.js` pour vérifier cohérence avant build
|
||||
- [ ] Tests unitaires pour fonctions d'extraction
|
||||
|
||||
**Impact** : Réduit les erreurs de build, améliore la maintenabilité
|
||||
|
||||
---
|
||||
|
||||
#### 1.2 Améliorer la détection navigation SPA (1 semaine)
|
||||
|
||||
**État actuel :**
|
||||
- `team-block-script.js` utilise `MutationObserver` pour détecter changements d'URL
|
||||
- Solution fonctionnelle mais peut avoir des latences
|
||||
|
||||
**Actions :**
|
||||
- [ ] Implémenter détection via `popstate` event (plus fiable)
|
||||
- [ ] Ajouter debounce pour éviter multiples initialisations
|
||||
- [ ] Tester navigation Next.js sur différentes versions
|
||||
|
||||
**Fichiers concernés :**
|
||||
- `public/team-block-script.js` (lignes 342-350)
|
||||
|
||||
---
|
||||
|
||||
#### 1.3 Optimiser les performances des visualisations (2 semaines)
|
||||
|
||||
**Problèmes identifiés :**
|
||||
- Graphe Cytoscape peut ralentir avec 50+ nœuds
|
||||
- Pas de lazy loading des données
|
||||
- Pas de cache côté client
|
||||
|
||||
**Actions :**
|
||||
- [ ] Implémenter pagination/filtrage pour graphe réseau
|
||||
- [ ] Ajouter cache localStorage pour `team-visualization-data.json`
|
||||
- [ ] Optimiser layout Cytoscape (réduire `numIter` pour perf)
|
||||
- [ ] Code splitting pour bibliothèques externes (Cytoscape, ECharts)
|
||||
|
||||
**Fichiers concernés :**
|
||||
- `public/team-block-script.js` (initNetwork, initCongestion)
|
||||
- `scripts/generate-team-visualization-data.js`
|
||||
|
||||
---
|
||||
|
||||
### 🟠 Priorité 2 : Fonctionnalités métier (Moyen terme - 3-6 mois)
|
||||
|
||||
#### 2.1 Edition via NetlifyCMS + Gitea (1 mois)
|
||||
|
||||
**Plan détaillé disponible :** `docs/app/plan-edition-cms.md`
|
||||
|
||||
**Actions :**
|
||||
- [ ] Créer `public/admin/index.html` et `config.yml`
|
||||
- [ ] Configurer OAuth Gitea
|
||||
- [ ] Ajouter bouton "Éditer" dans cartes de profil
|
||||
- [ ] Intégrer régénération dans CI/CD
|
||||
- [ ] Gérer CORS si nécessaire (proxy OAuth)
|
||||
|
||||
**Fichiers à créer/modifier :**
|
||||
- `public/admin/index.html`
|
||||
- `public/admin/config.yml`
|
||||
- `public/team-block-script.js` (ajout lien édition)
|
||||
- `.gitlab-ci.yml` (étape régénération)
|
||||
|
||||
---
|
||||
|
||||
#### 2.2 Export/Import de données (2 semaines)
|
||||
|
||||
**Opportunité identifiée :**
|
||||
- Scripts d'extraction existants mais pas d'export structuré
|
||||
- Pas de format d'échange standard
|
||||
|
||||
**Actions :**
|
||||
- [ ] Créer script `export-team-data.js` (CSV/JSON/Excel)
|
||||
- [ ] Ajouter import depuis CSV pour profils membres
|
||||
- [ ] Générer rapports PDF automatiques
|
||||
- [ ] API REST simple pour export (si besoin backend)
|
||||
|
||||
**Scripts à créer :**
|
||||
- `scripts/export-team-data.js`
|
||||
- `scripts/import-profiles.js`
|
||||
- `scripts/generate-pdf-report.js`
|
||||
|
||||
---
|
||||
|
||||
#### 2.3 Analytics et métriques avancées (3 semaines)
|
||||
|
||||
**Fonctionnalités existantes à étendre :**
|
||||
- `analyze-business-metrics.js` existe mais limité
|
||||
- Pas de visualisation des métriques dans l'UI
|
||||
|
||||
**Actions :**
|
||||
- [ ] Ajouter dashboard de métriques dans page équipe
|
||||
- [ ] Graphiques d'évolution temporelle (si historique disponible)
|
||||
- [ ] Alertes automatiques sur gaps critiques
|
||||
- [ ] Export des métriques en format standardisé
|
||||
|
||||
**Fichiers à créer/modifier :**
|
||||
- `scripts/generate-metrics-dashboard.js`
|
||||
- `public/team-block-script.js` (nouveau onglet métriques)
|
||||
- `scripts/analyze-business-metrics.js` (enrichir)
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Priorité 3 : Expérience utilisateur (Moyen terme - 6-9 mois)
|
||||
|
||||
#### 3.1 Recherche et filtrage avancés (2 semaines)
|
||||
|
||||
**Fonctionnalité manquante identifiée :**
|
||||
- Pas de recherche dans les profils membres
|
||||
- Pas de filtrage dans les visualisations
|
||||
|
||||
**Actions :**
|
||||
- [ ] Ajouter barre de recherche dans page équipe
|
||||
- [ ] Filtres par compétence, disponibilité, séniorité
|
||||
- [ ] Recherche full-text dans descriptions
|
||||
- [ ] Sauvegarder filtres dans URL (partageable)
|
||||
|
||||
**Fichiers à modifier :**
|
||||
- `public/team-block-script.js`
|
||||
- `scripts/generate-team-visualization-data.js` (index de recherche)
|
||||
|
||||
---
|
||||
|
||||
#### 3.2 Responsive design et accessibilité (3 semaines)
|
||||
|
||||
**Problèmes identifiés :**
|
||||
- Visualisations Cytoscape/ECharts peuvent être difficiles sur mobile
|
||||
- Pas de support clavier pour navigation
|
||||
- Couleurs peuvent poser problème (daltonisme)
|
||||
|
||||
**Actions :**
|
||||
- [ ] Adapter layouts pour petits écrans
|
||||
- [ ] Ajouter navigation clavier (Tab, Enter, Escape)
|
||||
- [ ] Palette de couleurs accessible (WCAG AA)
|
||||
- [ ] Mode sombre/clair
|
||||
- [ ] Support lecteur d'écran (ARIA labels)
|
||||
|
||||
**Fichiers concernés :**
|
||||
- `public/team-block-script.js` (CSS et interactions)
|
||||
- `custom.css`
|
||||
|
||||
---
|
||||
|
||||
#### 3.3 Notifications et alertes temps réel (4 semaines)
|
||||
|
||||
**Opportunité :**
|
||||
- Détecter automatiquement les changements critiques
|
||||
- Alerter sur gaps de compétences
|
||||
|
||||
**Actions :**
|
||||
- [ ] Système de webhooks pour changements Git
|
||||
- [ ] Notifications push (si service worker)
|
||||
- [ ] Dashboard d'alertes
|
||||
- [ ] Email automatique sur seuils critiques
|
||||
|
||||
---
|
||||
|
||||
### 🟢 Priorité 4 : Innovation et différenciation (Long terme - 9-12 mois)
|
||||
|
||||
#### 4.1 IA pour recommandations d'équipe (2 mois)
|
||||
|
||||
**Opportunité identifiée dans stratégie :**
|
||||
- Section "Knowledge Management + Private AI" dans stratégie
|
||||
- Graph RAG mentionné dans `strategie-script.js`
|
||||
|
||||
**Actions :**
|
||||
- [ ] Analyser profils avec LLM local (Mistral, Llama)
|
||||
- [ ] Recommandations automatiques pour équipe genèse
|
||||
- [ ] Détection de patterns dans compétences
|
||||
- [ ] Prédiction de gaps futurs
|
||||
|
||||
**Prérequis :**
|
||||
- Infrastructure ThreeFold pour IA privée
|
||||
- Modèles LLM open source
|
||||
|
||||
---
|
||||
|
||||
#### 4.2 Visualisation avancée : Web of Trust (3 mois)
|
||||
|
||||
**Mentionné dans stratégie :**
|
||||
- Cytoscape.js déjà intégré
|
||||
- Web of Trust visualisé dans roadmap
|
||||
|
||||
**Actions :**
|
||||
- [ ] Intégrer données Duniter/Ğ1 (si API disponible)
|
||||
- [ ] Graphe de confiance interactif
|
||||
- [ ] Analyse de clusters
|
||||
- [ ] Export pour analyses externes
|
||||
|
||||
---
|
||||
|
||||
#### 4.3 API REST complète (2 mois)
|
||||
|
||||
**Évolution naturelle :**
|
||||
- Passer d'app statique à hybride
|
||||
- Exposer données via API
|
||||
|
||||
**Actions :**
|
||||
- [ ] Backend Node.js/Express ou Rust
|
||||
- [ ] Endpoints REST pour données radar/équipe
|
||||
- [ ] GraphQL optionnel
|
||||
- [ ] Documentation OpenAPI/Swagger
|
||||
- [ ] Rate limiting et authentification
|
||||
|
||||
**Architecture proposée :**
|
||||
```
|
||||
Frontend (Next.js) ←→ API REST ←→ Base de données
|
||||
↓
|
||||
Scripts generation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dépendances techniques
|
||||
|
||||
### Bibliothèques externes actuelles
|
||||
|
||||
| Bibliothèque | Usage | Version | Maintenance |
|
||||
|--------------|-------|---------|-------------|
|
||||
| Cytoscape.js | Graphe réseau équipe | 3.26.0 (CDN) | ✅ Active |
|
||||
| ECharts | Matrice congestion | 5.4.3 (CDN) | ✅ Active |
|
||||
| Next.js | Framework principal | 14.2.4 | ✅ Active |
|
||||
| aoe_technology_radar | Base du radar | 4.4.0 | ✅ Active |
|
||||
|
||||
### Opportunités de migration
|
||||
|
||||
- [ ] **Cytoscape** : Considérer migration vers version locale (npm) pour meilleur contrôle
|
||||
- [ ] **ECharts** : Déjà bien intégré, pas de migration nécessaire
|
||||
- [ ] **Next.js** : Évaluer migration vers App Router (v13+) pour meilleures perfs
|
||||
|
||||
---
|
||||
|
||||
## Métriques et KPIs techniques
|
||||
|
||||
### Métriques à suivre (basées sur code actuel)
|
||||
|
||||
| Métrique | Cible | Mesure actuelle |
|
||||
|----------|-------|-----------------|
|
||||
| Temps de build | < 2 min | ~35-40s (OK) |
|
||||
| Taille bundle JS | < 200 KB | ~100 KB (OK) |
|
||||
| Temps chargement page team | < 2s | ~1-2s (OK) |
|
||||
| Couverture tests | > 80% | 0% (⚠️ À ajouter) |
|
||||
| Nombre de scripts | 9 | 9 scripts (OK) |
|
||||
|
||||
### Actions pour améliorer métriques
|
||||
|
||||
- [ ] Ajouter tests unitaires (Jest/Vitest)
|
||||
- [ ] Tests E2E pour visualisations (Playwright)
|
||||
- [ ] Monitoring performances (Web Vitals)
|
||||
- [ ] Analyse de bundle (webpack-bundle-analyzer)
|
||||
|
||||
---
|
||||
|
||||
## Fichiers critiques à maintenir
|
||||
|
||||
### Scripts de génération (priorité haute)
|
||||
|
||||
1. **`scripts/generate-team-visualization-data.js`**
|
||||
- Génère toutes les données de visualisation
|
||||
- Dépendance : Structure YAML des profils
|
||||
- Risque : Changements de format cassent la génération
|
||||
|
||||
2. **`scripts/analyze-business-metrics.js`**
|
||||
- Génère rapports stratégiques
|
||||
- Dépendance : Format des blips radar-business
|
||||
|
||||
3. **`public/team-block-script.js`**
|
||||
- Logique principale page équipe
|
||||
- Complexité élevée (500+ lignes)
|
||||
- Risque : Bugs navigation SPA
|
||||
|
||||
### Scripts d'infrastructure
|
||||
|
||||
- `docker-compose.business.yml` : Configuration déploiement
|
||||
- `Dockerfile.business` : Build containerisé
|
||||
- `.gitlab-ci.yml` : CI/CD (si configuré)
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention identifiés dans le code
|
||||
|
||||
### 1. Parsing YAML manuel (risque élevé)
|
||||
|
||||
**Fichier :** `scripts/generate-team-visualization-data.js`
|
||||
**Lignes :** 12-96
|
||||
**Problème :** Regex fragiles, peuvent échouer sur formats non standards
|
||||
|
||||
**Solution recommandée :** Intégrer `js-yaml` ou `gray-matter` (déjà en dépendance)
|
||||
|
||||
### 2. Protection mot de passe en clair
|
||||
|
||||
**Fichier :** `public/strategie-script.js`
|
||||
**Ligne :** 101
|
||||
**Problème :** Mot de passe hardcodé dans code client
|
||||
|
||||
**Solution recommandée :** Hash côté serveur ou authentification OAuth
|
||||
|
||||
### 3. Gestion navigation SPA complexe
|
||||
|
||||
**Fichier :** `public/team-block-script.js`
|
||||
**Lignes :** 342-350
|
||||
**Problème :** MutationObserver peut avoir des latences
|
||||
|
||||
**Solution recommandée :** Utiliser router Next.js ou événements natifs plus fiables
|
||||
|
||||
### 4. Pas de gestion d'erreurs robuste
|
||||
|
||||
**Fichiers :** Tous les scripts
|
||||
**Problème :** Try/catch basiques, pas de logging structuré
|
||||
|
||||
**Solution recommandée :** Intégrer logger (Winston, Pino) et gestion d'erreurs centralisée
|
||||
|
||||
---
|
||||
|
||||
## Évolution architecture proposée
|
||||
|
||||
### Étape 1 : Amélioration progressive (0-6 mois)
|
||||
```
|
||||
Application Statique
|
||||
↓
|
||||
+ Validation données
|
||||
+ Tests automatiques
|
||||
+ Monitoring
|
||||
```
|
||||
|
||||
### Étape 2 : Hybridation (6-12 mois)
|
||||
```
|
||||
Application Statique
|
||||
↓
|
||||
+ API REST simple (Node.js)
|
||||
+ NetlifyCMS pour édition
|
||||
+ Webhooks CI/CD
|
||||
```
|
||||
|
||||
### Étape 3 : Plateforme complète (12+ mois)
|
||||
```
|
||||
Frontend (Next.js)
|
||||
↓
|
||||
API Gateway (Rust/Node)
|
||||
↓
|
||||
Services: Data | Analytics | IA
|
||||
↓
|
||||
ThreeFold Infrastructure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Plan d'action immédiat (prochaines 4 semaines)
|
||||
|
||||
### Semaine 1-2 : Consolidation
|
||||
- [ ] Migrer parsing YAML vers librairie
|
||||
- [ ] Ajouter validation JSON Schema
|
||||
- [ ] Créer script de tests basiques
|
||||
|
||||
### Semaine 3-4 : Amélioration UX
|
||||
- [ ] Recherche dans profils
|
||||
- [ ] Filtres dans visualisations
|
||||
- [ ] Optimisation performances
|
||||
|
||||
---
|
||||
|
||||
## Estimation des efforts
|
||||
|
||||
| Priorité | Épics | Effort estimé | Complexité |
|
||||
|----------|-------|---------------|------------|
|
||||
| P1 | Consolidation | 4-6 semaines | Moyenne |
|
||||
| P2 | Fonctionnalités métier | 8-12 semaines | Élevée |
|
||||
| P3 | UX/UI | 6-9 semaines | Moyenne |
|
||||
| P4 | Innovation | 12-16 semaines | Très élevée |
|
||||
|
||||
**Total estimé :** 30-43 semaines (~7-10 mois de développement)
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
La roadmap est basée sur l'analyse du code source existant et identifie des améliorations réalistes et prioritaires. Les fonctionnalités core sont solides, mais il existe des opportunités d'amélioration significatives dans :
|
||||
|
||||
1. **Robustesse** : Validation, tests, gestion d'erreurs
|
||||
2. **Fonctionnalités** : Édition, export/import, analytics
|
||||
3. **UX** : Recherche, filtres, accessibilité
|
||||
4. **Innovation** : IA, visualisations avancées, API
|
||||
|
||||
L'architecture actuelle permet une évolution progressive sans refonte majeure.
|
||||
|
||||
---
|
||||
|
||||
*Document généré le 2025-12-09*
|
||||
*Prochaine révision recommandée : Après implémentation P1*
|
||||
|
||||
303
docs/app/troubleshooting.md
Normal file
303
docs/app/troubleshooting.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Guide de dépannage
|
||||
|
||||
## Problèmes courants et solutions
|
||||
|
||||
### Navigation et liens
|
||||
|
||||
#### Doublons de liens dans la navigation
|
||||
|
||||
**Symptôme** : Plusieurs liens "Équipe" ou autres liens apparaissent en double dans le header.
|
||||
|
||||
**Causes possibles** :
|
||||
- Le script Python a ajouté le lien plusieurs fois
|
||||
- Les scripts JavaScript ajoutent encore des liens (fonctions non désactivées)
|
||||
- Le fichier Navigation.tsx contient déjà des liens dupliqués
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier que les fonctions JavaScript sont bien désactivées :
|
||||
- `public/strategie-script.js` : `addLinksToHeader()` doit être commentée
|
||||
- `public/strategie-link.js` : `addStrategyLinkToHeader()` doit être commentée
|
||||
2. Rebuild l'image Docker avec `--no-cache` pour forcer l'exécution du script Python de nettoyage
|
||||
3. Vérifier dans les logs Docker que le script Python a bien supprimé les doublons
|
||||
|
||||
**Vérification** :
|
||||
```bash
|
||||
# Dans le conteneur
|
||||
grep -c 'href="/team"' radar-app/src/components/Navigation/Navigation.tsx
|
||||
# Doit retourner 1 (un seul lien)
|
||||
```
|
||||
|
||||
#### Lien "Équipe" n'apparaît pas
|
||||
|
||||
**Symptôme** : Le lien "👥 Équipe" n'est pas visible dans la navigation.
|
||||
|
||||
**Causes possibles** :
|
||||
- Le script Python n'a pas été exécuté
|
||||
- Le fichier Navigation.tsx n'a pas été trouvé
|
||||
- Erreur dans le script Python
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier les logs Docker lors du build pour voir si le script Python s'est exécuté
|
||||
2. Vérifier que le fichier `radar-app/src/components/Navigation/Navigation.tsx` existe
|
||||
3. Vérifier que le script Python a bien trouvé l'emplacement pour insérer le lien
|
||||
4. Rebuild avec `--no-cache` pour forcer l'exécution
|
||||
|
||||
**Vérification** :
|
||||
```bash
|
||||
# Dans le conteneur
|
||||
grep 'href="/team"' radar-app/src/components/Navigation/Navigation.tsx
|
||||
# Doit retourner le lien
|
||||
```
|
||||
|
||||
### Données du radar
|
||||
|
||||
#### Seulement 2 blips affichés (Ansible et OpenTofu)
|
||||
|
||||
**Symptôme** : Le radar n'affiche que quelques blips au lieu de tous.
|
||||
|
||||
**Causes possibles** :
|
||||
- Les blips utilisent des rings non définis dans la config (core, strategic, support au lieu de adopt, trial, assess, hold)
|
||||
- Les données business n'ont pas été copiées correctement
|
||||
- La config business n'est pas utilisée
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier que tous les blips utilisent les rings standards : `adopt`, `trial`, `assess`, `hold`
|
||||
2. Vérifier que `radar-business/config-business.json` contient bien les rings standards
|
||||
3. Vérifier dans les logs Docker que les données ont été copiées :
|
||||
```bash
|
||||
# Dans le conteneur
|
||||
find radar-app/data/radar -name "*.md" | wc -l
|
||||
# Doit retourner ~38 fichiers
|
||||
```
|
||||
4. Vérifier que `config-business.json` a été copié vers `radar-app/data/config.json`
|
||||
|
||||
**Migration des rings** :
|
||||
```bash
|
||||
# Migrer tous les blips vers les rings standards
|
||||
cd radar-business/2025-01-15
|
||||
find . -name "*.md" -exec sed -i 's/^ring: core$/ring: adopt/' {} \;
|
||||
find . -name "*.md" -exec sed -i 's/^ring: strategic$/ring: assess/' {} \;
|
||||
find . -name "*.md" -exec sed -i 's/^ring: support$/ring: adopt/' {} \;
|
||||
```
|
||||
|
||||
#### Erreur "invalid ring"
|
||||
|
||||
**Symptôme** : Des erreurs "invalid ring" apparaissent dans les logs.
|
||||
|
||||
**Cause** : Les blips utilisent des rings qui ne sont pas définis dans la configuration.
|
||||
|
||||
**Solution** : Vérifier que tous les rings utilisés dans les blips sont bien définis dans `config-business.json`.
|
||||
|
||||
### Page Equipe
|
||||
|
||||
#### Page `/team` affiche le radar au lieu des visualisations
|
||||
|
||||
**Symptome** : La page `/team` affiche le contenu du radar au lieu des visualisations equipe.
|
||||
|
||||
**Causes possibles** :
|
||||
- Le script `team-block-script.js` n'est pas charge
|
||||
- Le script `patch_document.py` n'a pas modifie `_document.tsx`
|
||||
- Le script n'a pas pu remplacer le DOM
|
||||
|
||||
**Solutions** :
|
||||
1. Verifier la console du navigateur (F12) pour les erreurs JavaScript
|
||||
2. Verifier que le script `team-block-script.js` est inclus dans le HTML :
|
||||
```bash
|
||||
curl -s http://localhost:3006/team/ | grep team-block-script
|
||||
```
|
||||
3. Rebuild avec `--no-cache` pour forcer l'execution des scripts Python
|
||||
|
||||
**Verification** :
|
||||
```bash
|
||||
# Dans le conteneur
|
||||
ls -l radar-app/public/team-block-script.js
|
||||
grep "team-block-script" radar-app/src/pages/_document.tsx
|
||||
```
|
||||
|
||||
#### Page `/team` retourne 404
|
||||
|
||||
**Symptome** : La page `/team` n'est pas accessible ou retourne une erreur 404.
|
||||
|
||||
**Causes possibles** :
|
||||
- La page Next.js `team.tsx` n'a pas ete creee
|
||||
- Le serveur utilise `--single` qui redirige vers index.html
|
||||
|
||||
**Solutions** :
|
||||
1. Verifier que le Dockerfile a bien cree `radar-app/src/pages/team.tsx`
|
||||
2. Verifier que `scripts/start-business.sh` ne contient pas l'option `--single`
|
||||
3. Verifier les logs du build Docker
|
||||
|
||||
**Verification** :
|
||||
```bash
|
||||
# Dans le conteneur
|
||||
ls -l radar-app/src/pages/team.tsx
|
||||
ls -l out/team/index.html
|
||||
```
|
||||
|
||||
#### Donnees de visualisation manquantes ou erreur de chargement
|
||||
|
||||
**Symptome** : Les visualisations de la page equipe sont vides ou affichent une erreur.
|
||||
|
||||
**Causes possibles** :
|
||||
- Le fichier `team-visualization-data.json` n'a pas ete genere
|
||||
- Le fichier n'est pas accessible depuis le navigateur
|
||||
- Erreur dans le chargement des bibliotheques (Cytoscape, ECharts)
|
||||
|
||||
**Solutions** :
|
||||
1. Generer les donnees :
|
||||
```bash
|
||||
node scripts/generate-team-visualization-data.js
|
||||
```
|
||||
2. Verifier que le fichier existe dans `public/team-visualization-data.json`
|
||||
3. Verifier l'acces direct : `http://localhost:3006/team-visualization-data.json`
|
||||
4. Verifier la console du navigateur pour les erreurs JavaScript
|
||||
5. Verifier la connexion internet (les bibliotheques sont chargees depuis CDN)
|
||||
|
||||
### Build Docker
|
||||
|
||||
#### Erreur "dockerfile parse error"
|
||||
|
||||
**Symptôme** : Erreur de syntaxe dans le Dockerfile.
|
||||
|
||||
**Causes possibles** :
|
||||
- Commandes shell mal formatées dans les instructions RUN
|
||||
- Variables mal échappées
|
||||
- Heredocs mal fermés
|
||||
|
||||
**Solution** : Vérifier la syntaxe du Dockerfile, notamment :
|
||||
- Les instructions RUN doivent être sur une seule ligne ou utiliser `\` pour continuer
|
||||
- Les heredocs doivent être correctement fermés
|
||||
- Les variables shell doivent être échappées avec `$$`
|
||||
|
||||
#### Build utilise le cache alors que les fichiers ont changé
|
||||
|
||||
**Symptôme** : Les modifications ne sont pas prises en compte après un rebuild.
|
||||
|
||||
**Cause** : Docker utilise le cache des couches précédentes.
|
||||
|
||||
**Solution** : Rebuild avec `--no-cache` :
|
||||
```bash
|
||||
docker build --no-cache -f Dockerfile.business -t laplank-techradar-radar-business .
|
||||
```
|
||||
|
||||
Dans Portainer, cocher l'option "No cache" lors du rebuild de la stack.
|
||||
|
||||
#### Script Python échoue avec exit code 2
|
||||
|
||||
**Symptôme** : Le script Python pour modifier Navigation.tsx échoue.
|
||||
|
||||
**Causes possibles** :
|
||||
- Le fichier Navigation.tsx n'existe pas encore
|
||||
- Problème de fin de ligne
|
||||
- Erreur de syntaxe Python
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier que le fichier existe avant d'exécuter le script
|
||||
2. Vérifier les logs pour voir l'erreur exacte
|
||||
3. Le script devrait afficher des messages de débogage
|
||||
|
||||
### Déploiement
|
||||
|
||||
#### Les modifications ne sont pas visibles après déploiement
|
||||
|
||||
**Symptôme** : Après un déploiement, les changements ne sont pas visibles.
|
||||
|
||||
**Causes possibles** :
|
||||
- Cache du navigateur
|
||||
- Cache Docker
|
||||
- Fichiers non copiés correctement
|
||||
|
||||
**Solutions** :
|
||||
1. Vider le cache du navigateur (Ctrl+Shift+R ou Cmd+Shift+R)
|
||||
2. Rebuild Docker avec `--no-cache`
|
||||
3. Vérifier les logs pour confirmer que les fichiers ont été copiés
|
||||
|
||||
#### Erreur de permissions dans le conteneur
|
||||
|
||||
**Symptôme** : Erreurs de permissions lors de l'exécution.
|
||||
|
||||
**Cause** : Problème de permissions sur les fichiers ou répertoires.
|
||||
|
||||
**Solution** : Vérifier les permissions dans le Dockerfile et s'assurer que les fichiers sont accessibles.
|
||||
|
||||
### Scripts
|
||||
|
||||
#### Script generate-team-visualization-data.js ne trouve pas les fichiers
|
||||
|
||||
**Symptôme** : Le script ne peut pas lire les fichiers de profils ou de technologies.
|
||||
|
||||
**Cause** : Chemins incorrects ou fichiers manquants.
|
||||
|
||||
**Solution** :
|
||||
1. Vérifier que les fichiers existent :
|
||||
- `docs/data/team/*.md`
|
||||
- `radar-business/2025-01-15/*.md`
|
||||
2. Exécuter le script depuis la racine du projet
|
||||
3. Vérifier les chemins dans le script
|
||||
|
||||
## Commandes utiles pour le débogage
|
||||
|
||||
### Verifier les fichiers dans le conteneur
|
||||
|
||||
```bash
|
||||
# Se connecter au conteneur
|
||||
docker exec -it <container-name> /bin/sh
|
||||
|
||||
# Verifier les fichiers de la page equipe
|
||||
ls -la radar-app/src/pages/team.tsx
|
||||
ls -la radar-app/public/team-block-script.js
|
||||
ls -la out/team-block-script.js
|
||||
ls -la out/team-visualization-data.json
|
||||
|
||||
# Verifier les modifications
|
||||
grep "team-block-script" radar-app/src/pages/_document.tsx
|
||||
ls -la radar-app/src/components/Navigation/Navigation.tsx
|
||||
|
||||
# Compter les blips
|
||||
find radar-app/data/radar -name "*.md" | wc -l
|
||||
|
||||
# Verifier la config
|
||||
head -60 radar-app/data/config.json
|
||||
```
|
||||
|
||||
### Vérifier les logs
|
||||
|
||||
```bash
|
||||
# Logs du conteneur
|
||||
docker logs <container-name>
|
||||
|
||||
# Logs en temps réel
|
||||
docker logs -f <container-name>
|
||||
|
||||
# Dernières lignes
|
||||
docker logs --tail=100 <container-name>
|
||||
```
|
||||
|
||||
### Vérifier les rings dans les blips
|
||||
|
||||
```bash
|
||||
# Lister tous les rings utilisés
|
||||
cd radar-business/2025-01-15
|
||||
grep -h "^ring:" *.md | sort | uniq -c
|
||||
|
||||
# Doit retourner uniquement : adopt, trial, assess, hold
|
||||
```
|
||||
|
||||
### Vérifier les liens dans Navigation.tsx
|
||||
|
||||
```bash
|
||||
# Compter les liens Équipe
|
||||
grep -c 'href="/team"' radar-app/src/components/Navigation/Navigation.tsx
|
||||
|
||||
# Voir le contexte autour du lien
|
||||
grep -A 3 -B 3 'href="/team"' radar-app/src/components/Navigation/Navigation.tsx
|
||||
```
|
||||
|
||||
## Obtenir de l'aide
|
||||
|
||||
Si le problème persiste :
|
||||
1. Vérifier les logs Docker complets
|
||||
2. Vérifier la documentation dans `docs/app/`
|
||||
3. Vérifier les issues sur le dépôt Git
|
||||
4. Contacter l'équipe technique
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
# Architecture du projet
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le projet AJR Technology Radar est une application web statique construite avec le framework `aoe_technology_radar`. L'application génère un site web interactif à partir de fichiers Markdown organisés par dates de release.
|
||||
|
||||
## Structure des répertoires
|
||||
|
||||
```
|
||||
TechradarDev/
|
||||
├── radar/ # Contenu du radar principal organisé par dates
|
||||
│ ├── 2017-03-01/ # Release de mars 2017
|
||||
│ ├── 2018-03-01/ # Release de mars 2018
|
||||
│ ├── 2019-11-01/ # Release de novembre 2019
|
||||
│ ├── 2021-07-01/ # Release de juillet 2021
|
||||
│ ├── 2022-03-28/ # Release de mars 2022
|
||||
│ ├── 2023-02-23/ # Release de février 2023
|
||||
│ ├── 2023-11-01/ # Release de novembre 2023
|
||||
│ ├── 2024-07-10/ # Release de juillet 2024
|
||||
│ └── 2025-04-10/ # Release d'avril 2025 (actuelle)
|
||||
├── radar-business/ # Contenu du radar business
|
||||
│ ├── 2025-01-15/ # Release business de janvier 2025
|
||||
│ ├── config-business.json # Configuration du radar business
|
||||
│ ├── FORMAT-BLIP.md # Format des blips business
|
||||
│ └── README.md # Documentation du radar business
|
||||
├── public/ # Fichiers statiques publics
|
||||
│ ├── images/ # Images utilisées dans les descriptions
|
||||
│ ├── logo.svg # Logo AJR
|
||||
│ ├── favicon.ico # Icône du site
|
||||
│ ├── robots.txt # Configuration pour les robots
|
||||
│ └── strategie-script.js # Script pour les pages de stratégie dynamiques
|
||||
├── scripts/ # Scripts utilitaires
|
||||
│ ├── serve-business.sh # Script pour servir le radar business en local
|
||||
│ ├── start-business.sh # Script de démarrage pour Docker
|
||||
│ ├── extract-technologies.js # Extraction des technologies depuis la doc
|
||||
│ └── analyze-business-metrics.js # Analyse des métriques business
|
||||
├── docker/ # Configuration Docker pour le déploiement
|
||||
│ ├── Dockerfile # Image Docker de production
|
||||
│ ├── docker-compose.yml # Configuration Docker Compose
|
||||
│ ├── docker-compose.labels.yml # Labels pour le déploiement
|
||||
│ └── docker-compose.local.yml # Configuration locale
|
||||
├── docs/ # Documentation du projet
|
||||
├── config.json # Configuration principale du radar
|
||||
├── config.json.backup # Backup de la config (généré par serve-business.sh)
|
||||
├── custom.css # Styles personnalisés
|
||||
├── about.md # Page "À propos" du radar
|
||||
├── package.json # Dépendances Node.js
|
||||
├── Dockerfile # Dockerfile alternatif (racine)
|
||||
├── Dockerfile.business # Dockerfile pour le radar business
|
||||
├── docker-compose.yml # Docker Compose alternatif (racine)
|
||||
├── docker-compose.business.yml # Docker Compose pour le radar business
|
||||
├── docker-entrypoint.sh # Script d'entrée Docker
|
||||
└── .drone.yml # Configuration CI/CD Drone
|
||||
```
|
||||
|
||||
## Format des fichiers radar
|
||||
|
||||
Chaque technologie (blip) est définie dans un fichier Markdown avec un en-tête YAML front matter :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: languages-and-frameworks|methods-and-patterns|platforms-and-aoe-services|tools
|
||||
tags: [tag1, tag2]
|
||||
---
|
||||
|
||||
Description de la technologie en Markdown.
|
||||
```
|
||||
|
||||
### Métadonnées
|
||||
|
||||
- **title** : Nom de la technologie
|
||||
- **ring** : Anneau du radar (adopt, trial, assess, hold)
|
||||
- **quadrant** : Quadrant du radar
|
||||
- **tags** : Tags pour le filtrage (optionnel)
|
||||
|
||||
## Flux de traitement
|
||||
|
||||
1. **Lecture des fichiers** : Le framework lit tous les fichiers `.md` dans les dossiers `radar/`
|
||||
2. **Parsing** : Extraction des métadonnées YAML et du contenu Markdown
|
||||
3. **Génération** : Création d'une application React statique
|
||||
4. **Build** : Compilation en fichiers HTML/CSS/JS statiques
|
||||
5. **Serve** : Service via un serveur web statique
|
||||
|
||||
## Dépendances principales
|
||||
|
||||
- **aoe_technology_radar** : Framework principal (dépendance GitHub)
|
||||
- **Node.js** : Runtime JavaScript (version 20+)
|
||||
- **npm** : Gestionnaire de paquets
|
||||
|
||||
## Configuration
|
||||
|
||||
La configuration principale se trouve dans `config.json` et définit :
|
||||
- Les quadrants et leurs descriptions
|
||||
- Les anneaux (rings) et leurs significations
|
||||
- Les couleurs et le style
|
||||
- Les options d'affichage
|
||||
- Les métadonnées du site
|
||||
|
||||
Voir [configuration.md](./configuration.md) pour plus de détails.
|
||||
|
||||
## Radar Business
|
||||
|
||||
Le Radar Business est une variante du radar principal avec :
|
||||
|
||||
- **Configuration spécifique** : `radar-business/config-business.json`
|
||||
- **Quadrants business** : Technologies Différenciantes, Commodité, Risque, Émergentes
|
||||
- **Anneaux stratégiques** : Core, Strategic, Support, Legacy
|
||||
- **Pages de stratégie** : Pages dynamiques générées via `public/strategie-script.js`
|
||||
- **Protection par mot de passe** : Authentification client-side (mot de passe : `laplank-radar`)
|
||||
- **Base path** : `/` (racine, pas de sous-chemin)
|
||||
|
||||
### Scripts de stratégie
|
||||
|
||||
Le fichier `public/strategie-script.js` contient :
|
||||
- La logique de protection par mot de passe
|
||||
- La conversion Markdown vers HTML pour les pages de stratégie
|
||||
- La gestion de la navigation dans le header
|
||||
- Le contenu des trois pages de stratégie :
|
||||
- Stratégie d'Évolution Technique
|
||||
- Stratégie Business
|
||||
- Opportunités DataViz Expert
|
||||
|
||||
## Build et déploiement
|
||||
|
||||
Le projet utilise plusieurs commandes :
|
||||
- `npm run build` : Génère les fichiers statiques du radar principal
|
||||
- `npm run serve` : Lance un serveur de développement du radar principal (port 3000)
|
||||
- `npm run serve-business` : Lance un serveur de développement du radar business (port 3004)
|
||||
|
||||
Le déploiement se fait via Docker avec plusieurs configurations selon l'environnement :
|
||||
- **Radar principal** : Via `docker/Dockerfile` ou `Dockerfile`
|
||||
- **Radar business** : Via `Dockerfile.business` et `docker-compose.business.yml` (Portainer)
|
||||
|
||||
Voir [deploiement.md](./deploiement.md) pour plus de détails.
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
# Configuration
|
||||
|
||||
## Fichier config.json
|
||||
|
||||
Le fichier `config.json` contient toute la configuration du radar. Il définit l'apparence, le comportement et la structure du radar.
|
||||
|
||||
## Structure de configuration
|
||||
|
||||
### Paramètres de base
|
||||
|
||||
```json
|
||||
{
|
||||
"basePath": "",
|
||||
"baseUrl": "",
|
||||
"editUrl": "https://git.open.us.org/syoul/TechradarDev/_edit/main/radar/{release}/{id}.md",
|
||||
"logoFile": "logo.svg",
|
||||
"jsFile": "strategie-script.js"
|
||||
}
|
||||
```
|
||||
|
||||
- **basePath** : Chemin de base pour l'application (vide `""` pour servir à la racine `/`)
|
||||
- **baseUrl** : URL de base du site
|
||||
- **editUrl** : Template d'URL pour éditer les fichiers (utilise {release} et {id})
|
||||
- **logoFile** : Nom du fichier logo dans `public/`
|
||||
- **jsFile** : Fichier JavaScript personnalisé à charger (optionnel, utilisé pour le radar business)
|
||||
|
||||
### Options d'affichage (toggles)
|
||||
|
||||
```json
|
||||
{
|
||||
"toggles": {
|
||||
"showChart": true,
|
||||
"showTagFilter": true,
|
||||
"showQuadrantList": true,
|
||||
"showEmptyRings": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **showChart** : Affiche le graphique radar interactif
|
||||
- **showTagFilter** : Active le filtre par tags
|
||||
- **showQuadrantList** : Affiche la liste des quadrants
|
||||
- **showEmptyRings** : Affiche les anneaux vides
|
||||
|
||||
### Sections
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": ["radar", "tags", "list"]
|
||||
}
|
||||
```
|
||||
|
||||
Définit l'ordre des sections dans l'interface.
|
||||
|
||||
### Couleurs
|
||||
|
||||
```json
|
||||
{
|
||||
"colors": {
|
||||
"foreground": "#fff",
|
||||
"background": "#173d7a",
|
||||
"highlight": "#029df7",
|
||||
"content": "#fff",
|
||||
"text": "#575757",
|
||||
"link": "#029df7",
|
||||
"border": "rgba(255, 255, 255, 0.1)",
|
||||
"tag": "rgba(255, 255, 255, 0.1)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Personnalisation des couleurs de l'interface.
|
||||
|
||||
### Quadrants
|
||||
|
||||
Les quadrants définissent les quatre catégories principales :
|
||||
|
||||
1. **Languages & Frameworks** : Langages et frameworks de développement
|
||||
2. **Methods & Patterns** : Méthodes et patterns de développement
|
||||
3. **Platforms & Operations** : Plateformes et opérations
|
||||
4. **Tools** : Outils de développement
|
||||
|
||||
Chaque quadrant a :
|
||||
- **id** : Identifiant unique
|
||||
- **title** : Titre affiché
|
||||
- **description** : Description du quadrant
|
||||
- **color** : Couleur associée
|
||||
|
||||
### Anneaux (Rings)
|
||||
|
||||
Les anneaux classifient les technologies selon leur niveau d'adoption :
|
||||
|
||||
1. **Adopt** : Recommandé, utilisé avec succès
|
||||
2. **Trial** : À essayer, prometteur
|
||||
3. **Assess** : À évaluer, à surveiller
|
||||
4. **Hold** : À éviter, à remplacer
|
||||
|
||||
Chaque anneau a :
|
||||
- **id** : Identifiant unique
|
||||
- **title** : Titre affiché
|
||||
- **description** : Description de l'anneau
|
||||
- **color** : Couleur associée
|
||||
- **radius** : Rayon dans le graphique (0-1)
|
||||
- **strokeWidth** : Épaisseur du trait
|
||||
|
||||
### Flags (Drapeaux)
|
||||
|
||||
Les flags marquent les changements entre versions :
|
||||
|
||||
- **new** : Nouveau dans cette version
|
||||
- **changed** : Modifié récemment
|
||||
- **default** : Inchangé
|
||||
|
||||
### Graphique
|
||||
|
||||
```json
|
||||
{
|
||||
"chart": {
|
||||
"size": 800,
|
||||
"blipSize": 12
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **size** : Taille du graphique en pixels
|
||||
- **blipSize** : Taille des points (blips) sur le graphique
|
||||
|
||||
### Labels (Textes)
|
||||
|
||||
Les labels permettent de personnaliser tous les textes de l'interface, notamment :
|
||||
- Titre du site
|
||||
- Textes des pages
|
||||
- Messages d'erreur
|
||||
- Placeholders
|
||||
- Footer
|
||||
|
||||
## Personnalisation
|
||||
|
||||
### Modifier les couleurs
|
||||
|
||||
Éditez la section `colors` dans `config.json` avec les codes hexadécimaux souhaités.
|
||||
|
||||
### Ajouter un quadrant
|
||||
|
||||
Ajoutez un nouvel objet dans le tableau `quadrants` avec les propriétés requises.
|
||||
|
||||
### Modifier les descriptions
|
||||
|
||||
Les descriptions des quadrants et anneaux peuvent être modifiées directement dans `config.json`.
|
||||
|
||||
### Styles personnalisés
|
||||
|
||||
Le fichier `custom.css` permet d'ajouter des styles CSS personnalisés qui seront appliqués à l'application.
|
||||
|
||||
## Configuration du Radar Business
|
||||
|
||||
Le Radar Business utilise une configuration spécifique dans `radar-business/config-business.json` :
|
||||
|
||||
### Différences principales
|
||||
|
||||
- **basePath** : `""` (vide) pour servir à la racine
|
||||
- **jsFile** : `"strategie-script.js"` pour charger le script de stratégie
|
||||
- **Quadrants business** : Technologies Différenciantes, Commodité, Risque, Émergentes
|
||||
- **Anneaux stratégiques** : Core, Strategic, Support, Legacy
|
||||
- **Couleurs** : Thème vert (`#1a4d3a` pour le background, `#2ecc71` pour les accents)
|
||||
|
||||
### Script de stratégie
|
||||
|
||||
Le fichier `public/strategie-script.js` est chargé automatiquement et fournit :
|
||||
- Protection par mot de passe (authentification client-side)
|
||||
- Pages de stratégie dynamiques (Markdown converti en HTML)
|
||||
- Navigation dans le header
|
||||
|
||||
## Variables d'environnement
|
||||
|
||||
En Docker, la variable `BASE_PATH` peut être utilisée pour modifier dynamiquement le `basePath` dans `config.json`. Le script `docker-entrypoint.sh` effectue cette modification au démarrage.
|
||||
|
||||
Pour le Radar Business, le `basePath` est fixé à `""` (vide) dans `config-business.json` pour servir l'application à la racine.
|
||||
|
||||
## Tags disponibles
|
||||
|
||||
Les tags suivants sont établis pour classifier les technologies :
|
||||
|
||||
- architecture
|
||||
- security
|
||||
- devops
|
||||
- frontend
|
||||
- agile
|
||||
- coding
|
||||
- quality assurance
|
||||
- ci/cd
|
||||
- ux/ui
|
||||
- documentation
|
||||
|
||||
Les tags sont utilisés dans les fichiers Markdown des blips et permettent le filtrage dans l'interface.
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
# Guide de contribution
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce guide explique comment contribuer au Technology Radar AJR en ajoutant, modifiant ou supprimant des technologies (blips).
|
||||
|
||||
## Processus de contribution
|
||||
|
||||
### 1. Préparer l'environnement
|
||||
|
||||
Voir le [guide de développement](./developpement.md) pour l'installation et la configuration de l'environnement local.
|
||||
|
||||
### 2. Créer une branche
|
||||
|
||||
```bash
|
||||
git checkout -b feature/nom-de-la-technologie
|
||||
```
|
||||
|
||||
### 3. Ajouter ou modifier un blip
|
||||
|
||||
#### Ajouter un nouveau blip
|
||||
|
||||
1. Créer un fichier Markdown dans le dossier de release approprié :
|
||||
```
|
||||
radar/2025-04-10/nom-technologie.md
|
||||
```
|
||||
|
||||
2. Utiliser le format standard :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: languages-and-frameworks|methods-and-patterns|platforms-and-aoe-services|tools
|
||||
tags: [tag1, tag2]
|
||||
---
|
||||
|
||||
Description de la technologie.
|
||||
|
||||
## Avantages
|
||||
|
||||
- Point 1
|
||||
- Point 2
|
||||
|
||||
## Cas d'usage AJR
|
||||
|
||||
Description de l'utilisation chez AJR.
|
||||
```
|
||||
|
||||
#### Modifier un blip existant
|
||||
|
||||
1. Localiser le fichier dans le dossier de release
|
||||
2. Modifier le contenu ou les métadonnées
|
||||
3. Si vous changez le ring ou le quadrant, documenter la raison
|
||||
|
||||
#### Supprimer un blip
|
||||
|
||||
Si une technologie doit être retirée du radar :
|
||||
- La déplacer vers le ring "hold" plutôt que de la supprimer
|
||||
- Ou la supprimer complètement si elle n'est plus pertinente
|
||||
|
||||
### 4. Tester localement
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Vérifier :
|
||||
- L'affichage correct du blip
|
||||
- Le positionnement dans le bon quadrant et ring
|
||||
- La lisibilité du contenu
|
||||
- Le fonctionnement des tags
|
||||
|
||||
### 5. Commiter les changements
|
||||
|
||||
```bash
|
||||
git add radar/2025-04-10/nom-technologie.md
|
||||
git commit -m "feat: ajouter [technologie] au quadrant [quadrant]"
|
||||
```
|
||||
|
||||
### 6. Pousser et créer une pull request
|
||||
|
||||
```bash
|
||||
git push origin feature/nom-de-la-technologie
|
||||
```
|
||||
|
||||
Créer une pull request sur le dépôt Git.
|
||||
|
||||
## Guidelines de contenu
|
||||
|
||||
### Choix du ring
|
||||
|
||||
- **Adopt** : Technologie utilisée avec succès dans plusieurs projets, stable et recommandée
|
||||
- **Trial** : Technologie testée avec succès, à considérer pour de nouveaux projets
|
||||
- **Assess** : Technologie prometteuse, à évaluer selon les besoins
|
||||
- **Hold** : Technologie à éviter ou à remplacer, mais peut être maintenue dans les projets existants
|
||||
|
||||
### Choix du quadrant
|
||||
|
||||
- **Languages & Frameworks** : Langages de programmation et frameworks de développement
|
||||
- **Methods & Patterns** : Méthodologies, patterns architecturaux, pratiques de développement
|
||||
- **Platforms & Operations** : Plateformes cloud, infrastructure, services opérationnels
|
||||
- **Tools** : Outils de développement, utilitaires, logiciels
|
||||
|
||||
### Tags
|
||||
|
||||
Utiliser les tags établis :
|
||||
- architecture
|
||||
- security
|
||||
- devops
|
||||
- frontend
|
||||
- agile
|
||||
- coding
|
||||
- quality assurance
|
||||
- ci/cd
|
||||
- ux/ui
|
||||
- documentation
|
||||
|
||||
Ajouter plusieurs tags si la technologie couvre plusieurs domaines.
|
||||
|
||||
### Qualité du contenu
|
||||
|
||||
- **Clarté** : Description claire et concise
|
||||
- **Pertinence** : Focus sur l'utilisation chez AJR
|
||||
- **Objectivité** : Présenter les avantages et inconvénients
|
||||
- **Concision** : Rester factuel et éviter les détails superflus
|
||||
|
||||
## Format des commits
|
||||
|
||||
Utiliser des messages de commit clairs :
|
||||
|
||||
```
|
||||
feat: ajouter [technologie] au quadrant [quadrant]
|
||||
fix: corriger la description de [technologie]
|
||||
update: déplacer [technologie] de trial à adopt
|
||||
docs: améliorer la documentation de [technologie]
|
||||
```
|
||||
|
||||
## Créer une nouvelle release
|
||||
|
||||
Quand créer une nouvelle release :
|
||||
|
||||
1. **Périodicité** : Généralement tous les 3-6 mois
|
||||
2. **Changements significatifs** : Plusieurs nouveaux blips ou changements majeurs
|
||||
3. **Événements** : Après des évaluations importantes
|
||||
|
||||
### Processus de release
|
||||
|
||||
1. Créer un nouveau dossier avec la date :
|
||||
```bash
|
||||
mkdir radar/2025-07-15
|
||||
```
|
||||
|
||||
2. Copier les blips pertinents depuis la release précédente
|
||||
|
||||
3. Ajouter les nouveaux blips
|
||||
|
||||
4. Mettre à jour les blips existants si nécessaire
|
||||
|
||||
5. Documenter les changements majeurs
|
||||
|
||||
## Review process
|
||||
|
||||
Les contributions sont revues pour :
|
||||
|
||||
- **Exactitude** : Les informations sont correctes
|
||||
- **Pertinence** : La technologie est pertinente pour AJR
|
||||
- **Format** : Le format Markdown est correct
|
||||
- **Classification** : Le ring et quadrant sont appropriés
|
||||
- **Qualité** : Le contenu est clair et utile
|
||||
|
||||
## Questions fréquentes
|
||||
|
||||
### Puis-je ajouter une technologie que je n'ai pas encore utilisée ?
|
||||
|
||||
Non. Le radar ne contient que des technologies testées au moins une fois par l'équipe.
|
||||
|
||||
### Comment décider entre deux quadrants ?
|
||||
|
||||
Choisir le quadrant le plus approprié. Si c'est ambigu, discuter avec l'équipe.
|
||||
|
||||
### Puis-je modifier un blip d'une release précédente ?
|
||||
|
||||
Généralement non. Les releases précédentes sont figées. Créer un nouveau blip dans la release actuelle si nécessaire.
|
||||
|
||||
### Comment gérer les technologies obsolètes ?
|
||||
|
||||
Les déplacer vers le ring "hold" avec une explication de pourquoi elles ne sont plus recommandées.
|
||||
|
||||
## Ressources
|
||||
|
||||
- [Guide de développement](./developpement.md)
|
||||
- [Configuration](./configuration.md)
|
||||
- [Architecture](./architecture.md)
|
||||
- Framework source : https://github.com/AOEpeople/aoe_technology_radar
|
||||
|
||||
## Contact
|
||||
|
||||
Pour toute question sur les contributions, contacter l'équipe AJR ou ouvrir une issue sur le dépôt Git.
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
# Guide de déploiement
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le projet peut être déployé de plusieurs façons :
|
||||
- Docker Compose (recommandé pour la production)
|
||||
- Docker simple
|
||||
- Build statique avec serveur web
|
||||
- Portainer (pour le Radar Business)
|
||||
|
||||
## Déploiement avec Docker
|
||||
|
||||
### Configuration Docker
|
||||
|
||||
Le projet contient plusieurs configurations Docker :
|
||||
|
||||
- `docker/Dockerfile` : Dockerfile principal avec multi-stage build
|
||||
- `docker/docker-compose.yml` : Configuration de base
|
||||
- `docker/docker-compose.labels.yml` : Labels pour le reverse proxy
|
||||
- `docker/docker-compose.local.yml` : Configuration pour développement local
|
||||
- `Dockerfile` (racine) : Dockerfile alternatif
|
||||
- `docker-compose.yml` (racine) : Docker Compose alternatif
|
||||
- `Dockerfile.business` : Dockerfile spécifique pour le Radar Business
|
||||
- `docker-compose.business.yml` : Docker Compose pour le Radar Business (Portainer)
|
||||
|
||||
### Build de l'image Docker
|
||||
|
||||
#### Avec le Dockerfile principal
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Avec build args
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg BASE_PATH="/techradar" \
|
||||
--build-arg UID=1000 \
|
||||
--build-arg GID=1000 \
|
||||
-f docker/Dockerfile \
|
||||
-t techradar:latest \
|
||||
.
|
||||
```
|
||||
|
||||
### Variables d'environnement
|
||||
|
||||
- **BASE_PATH** : Chemin de base pour l'application (défaut: `/`)
|
||||
- **UID** : User ID pour l'utilisateur dans le conteneur (défaut: 1000)
|
||||
- **GID** : Group ID pour l'utilisateur dans le conteneur (défaut: 1000)
|
||||
|
||||
### Démarrage avec Docker Compose
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
L'application sera accessible sur le port 3000.
|
||||
|
||||
### Configuration du basePath
|
||||
|
||||
Le script `docker-entrypoint.sh` modifie dynamiquement le `basePath` dans `config.json` au démarrage du conteneur en utilisant la variable d'environnement `BASE_PATH`.
|
||||
|
||||
## Déploiement statique
|
||||
|
||||
### Build des fichiers statiques
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Les fichiers sont générés dans le répertoire `build/`.
|
||||
|
||||
### Servir avec un serveur web
|
||||
|
||||
#### Nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name coeurbox.syoul.fr;
|
||||
root /chemin/vers/build;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Apache
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName coeurbox.syoul.fr
|
||||
DocumentRoot /chemin/vers/build
|
||||
|
||||
<Directory /chemin/vers/build>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## Déploiement du Radar Business avec Portainer
|
||||
|
||||
Le Radar Business est déployé via Portainer en utilisant une stack Docker Compose.
|
||||
|
||||
### Configuration Portainer
|
||||
|
||||
1. **Créer une nouvelle stack** dans Portainer
|
||||
2. **Nom de la stack** : `laplank-radar-business` (ou autre nom)
|
||||
3. **Méthode** : Git Repository
|
||||
4. **Repository URL** : URL du dépôt Git (ex: `https://git.open.us.org/AJR/TechradarDev.git`)
|
||||
5. **Branch** : `dev-biz` (ou la branche appropriée)
|
||||
6. **Compose path** : `docker-compose.business.yml`
|
||||
|
||||
### Configuration Docker Compose Business
|
||||
|
||||
Le fichier `docker-compose.business.yml` configure :
|
||||
- **Port** : 3004 (mappé depuis le port 3000 du conteneur)
|
||||
- **Image** : Construite depuis `Dockerfile.business`
|
||||
- **Environnement** : `NODE_ENV=production`
|
||||
- **Restart policy** : `unless-stopped`
|
||||
|
||||
### Dockerfile Business
|
||||
|
||||
Le `Dockerfile.business` :
|
||||
- Utilise Node.js 20 Alpine
|
||||
- Configure les variables d'environnement pour désactiver Husky
|
||||
- Installe les dépendances avec `--ignore-scripts`
|
||||
- Patche le package `aoe_technology_radar` pour inclure `gray-matter`
|
||||
- Pré-installe `.techradar` pendant le build
|
||||
- Applique la configuration business (`config-business.json`)
|
||||
- Expose le port 3000
|
||||
- Démarre via `scripts/start-business.sh`
|
||||
|
||||
### Authentification Git pour Portainer
|
||||
|
||||
Si le dépôt est privé, utiliser un **Personal Access Token** (Gitea) :
|
||||
1. Créer un token dans Gitea avec les permissions de lecture
|
||||
2. Utiliser l'URL : `https://<token>@git.open.us.org/AJR/TechradarDev.git`
|
||||
|
||||
### Mise à jour
|
||||
|
||||
Pour mettre à jour le Radar Business dans Portainer :
|
||||
1. **Pull latest image** : Dans Portainer, utiliser "Pull latest image"
|
||||
2. **Rebuild** : Ou reconstruire la stack depuis Git
|
||||
|
||||
## Déploiement avec Drone CI
|
||||
|
||||
Le projet est configuré pour le déploiement automatique via Drone CI (`.drone.yml`).
|
||||
|
||||
### Pipeline de déploiement
|
||||
|
||||
1. **Build** : Construction de l'image Docker
|
||||
2. **Déploiement** : Lancement du conteneur avec Docker Compose
|
||||
3. **Notification** : Envoi d'une notification Telegram
|
||||
|
||||
### Configuration Drone
|
||||
|
||||
Le pipeline utilise :
|
||||
- Variables d'environnement dynamiques basées sur le dépôt Git
|
||||
- Labels pour le reverse proxy (Traefik)
|
||||
- Notifications Telegram en cas de succès/échec
|
||||
|
||||
### Variables d'environnement Drone
|
||||
|
||||
- `DRONE_REPO_OWNER` : Propriétaire du dépôt
|
||||
- `DRONE_REPO_NAME` : Nom du dépôt
|
||||
- `DRONE_COMMIT_BRANCH` : Branche du commit
|
||||
|
||||
Ces variables sont utilisées pour générer le `BASE_PATH` dynamiquement.
|
||||
|
||||
## Déploiement en production
|
||||
|
||||
### Étapes recommandées
|
||||
|
||||
1. **Préparer l'environnement**
|
||||
```bash
|
||||
git clone https://git.open.us.org/AJR/TechradarDev.git
|
||||
cd TechradarDev
|
||||
```
|
||||
|
||||
2. **Configurer les variables**
|
||||
- Définir `BASE_PATH` selon votre configuration
|
||||
- Ajuster les ports si nécessaire
|
||||
|
||||
3. **Build et démarrage**
|
||||
```bash
|
||||
cd docker
|
||||
docker compose -f docker-compose.yml -f docker-compose.labels.yml up -d --build
|
||||
```
|
||||
|
||||
4. **Vérifier le déploiement**
|
||||
- Accéder à l'URL configurée
|
||||
- Vérifier les logs : `docker compose logs -f`
|
||||
|
||||
### Reverse proxy
|
||||
|
||||
Le projet est configuré pour fonctionner derrière un reverse proxy (Traefik) via les labels dans `docker-compose.labels.yml`.
|
||||
|
||||
### Sécurité
|
||||
|
||||
- Utiliser HTTPS en production
|
||||
- Configurer les headers de sécurité appropriés
|
||||
- Limiter l'accès si nécessaire
|
||||
- Surveiller les logs
|
||||
|
||||
## Mise à jour
|
||||
|
||||
### Mettre à jour le contenu
|
||||
|
||||
1. Modifier les fichiers dans `radar/`
|
||||
2. Rebuild l'image :
|
||||
```bash
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Mettre à jour les dépendances
|
||||
|
||||
1. Modifier `package.json` si nécessaire
|
||||
2. Rebuild l'image complète
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Logs Docker
|
||||
|
||||
```bash
|
||||
# Voir les logs
|
||||
docker compose logs -f
|
||||
|
||||
# Logs du dernier démarrage
|
||||
docker compose logs --tail=100
|
||||
```
|
||||
|
||||
### Santé de l'application
|
||||
|
||||
**Radar Principal** : Expose le port 3000. Vérifier avec :
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/techradar
|
||||
```
|
||||
|
||||
**Radar Business** : Expose le port 3004 (mappé depuis 3000). Vérifier avec :
|
||||
|
||||
```bash
|
||||
curl http://localhost:3004/
|
||||
```
|
||||
|
||||
Note : Le Radar Business est protégé par un mot de passe, donc la réponse peut être l'écran d'authentification.
|
||||
|
||||
## Rollback
|
||||
|
||||
En cas de problème, revenir à une version précédente :
|
||||
|
||||
```bash
|
||||
# Arrêter le conteneur actuel
|
||||
docker compose down
|
||||
|
||||
# Checkout une version précédente
|
||||
git checkout <commit-hash>
|
||||
|
||||
# Rebuild et redémarrer
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Problème de basePath
|
||||
|
||||
Si l'application ne se charge pas correctement :
|
||||
- Vérifier la variable `BASE_PATH`
|
||||
- Vérifier les logs du conteneur
|
||||
- Vérifier la configuration du reverse proxy
|
||||
|
||||
### Problème de permissions
|
||||
|
||||
Si des erreurs de permissions apparaissent :
|
||||
- Vérifier les UID/GID dans le Dockerfile
|
||||
- Vérifier les permissions des volumes montés
|
||||
|
||||
### Problème de build
|
||||
|
||||
Si le build échoue :
|
||||
- Vérifier la version de Node.js
|
||||
- Vérifier les dépendances npm
|
||||
- Nettoyer le cache : `docker system prune -a`
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
# Guide de développement
|
||||
|
||||
## Prérequis
|
||||
|
||||
- **Node.js** : Version 20 ou supérieure
|
||||
- **npm** : Gestionnaire de paquets Node.js
|
||||
- **Git** : Pour cloner et gérer le dépôt
|
||||
|
||||
## Installation
|
||||
|
||||
### Cloner le dépôt
|
||||
|
||||
```bash
|
||||
git clone https://git.open.us.org/AJR/TechradarDev.git
|
||||
cd TechradarDev
|
||||
```
|
||||
|
||||
### Installer les dépendances
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Cette commande installe le framework `aoe_technology_radar` depuis GitHub.
|
||||
|
||||
## Développement local
|
||||
|
||||
### Démarrer le serveur de développement (Radar Principal)
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3000/techradar
|
||||
|
||||
### Démarrer le serveur de développement (Radar Business)
|
||||
|
||||
```bash
|
||||
npm run serve-business
|
||||
```
|
||||
|
||||
Le serveur démarre sur http://localhost:3004
|
||||
|
||||
**Note importante** : Le script `serve-business.sh` :
|
||||
- Sauvegarde temporairement `config.json` et le dossier `radar/`
|
||||
- Copie `radar-business/config-business.json` vers `config.json`
|
||||
- Copie `radar-business/2025-01-15/` vers `radar/`
|
||||
- Restaure la configuration originale à la sortie (Ctrl+C)
|
||||
|
||||
Le Radar Business est protégé par un mot de passe : `laplank-radar`
|
||||
|
||||
### Build de production
|
||||
|
||||
Pour générer les fichiers statiques :
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Les fichiers générés sont créés dans le répertoire `build/` (généré par le framework).
|
||||
|
||||
## Structure des fichiers radar
|
||||
|
||||
### Créer un nouveau blip
|
||||
|
||||
1. Créer un nouveau fichier Markdown dans le dossier de release approprié :
|
||||
```
|
||||
radar/2025-04-10/nom-technologie.md
|
||||
```
|
||||
|
||||
2. Utiliser le format suivant :
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Nom de la technologie"
|
||||
ring: adopt|trial|assess|hold
|
||||
quadrant: languages-and-frameworks|methods-and-patterns|platforms-and-aoe-services|tools
|
||||
tags: [tag1, tag2]
|
||||
---
|
||||
|
||||
Description de la technologie en Markdown.
|
||||
|
||||
## Avantages
|
||||
|
||||
- Point 1
|
||||
- Point 2
|
||||
|
||||
## Cas d'usage
|
||||
|
||||
Description des cas d'usage chez AJR.
|
||||
```
|
||||
|
||||
### Format des métadonnées
|
||||
|
||||
- **title** (obligatoire) : Nom de la technologie
|
||||
- **ring** (obligatoire) : `adopt`, `trial`, `assess`, ou `hold`
|
||||
- **quadrant** (obligatoire) : Un des quatre quadrants définis dans `config.json`
|
||||
- **tags** (optionnel) : Tableau de tags pour le filtrage
|
||||
|
||||
### Exemple complet
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "Docker"
|
||||
ring: trial
|
||||
quadrant: tools
|
||||
tags: [devops, ci/cd]
|
||||
---
|
||||
|
||||
Docker est une plateforme de conteneurisation qui permet de packager des applications avec leurs dépendances.
|
||||
|
||||
## Avantages
|
||||
|
||||
- Isolation des environnements
|
||||
- Portabilité entre environnements
|
||||
- Facilite le déploiement
|
||||
|
||||
## Utilisation chez AJR
|
||||
|
||||
Nous utilisons Docker pour containeriser nos applications et faciliter le déploiement.
|
||||
```
|
||||
|
||||
## Modifier un blip existant
|
||||
|
||||
1. Localiser le fichier dans le dossier de release approprié
|
||||
2. Modifier le contenu Markdown
|
||||
3. Si nécessaire, modifier les métadonnées (ring, quadrant, tags)
|
||||
4. Tester localement avec `npm run serve`
|
||||
|
||||
## Créer une nouvelle release
|
||||
|
||||
1. Créer un nouveau dossier avec la date au format `YYYY-MM-DD` :
|
||||
```bash
|
||||
mkdir radar/2025-07-15
|
||||
```
|
||||
|
||||
2. Copier les blips pertinents depuis la release précédente ou créer de nouveaux blips
|
||||
|
||||
3. Mettre à jour les blips existants si nécessaire
|
||||
|
||||
## Ajouter des images
|
||||
|
||||
1. Placer les images dans `public/images/`
|
||||
2. Référencer dans les fichiers Markdown :
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
## Personnalisation CSS
|
||||
|
||||
Le fichier `custom.css` permet d'ajouter des styles personnalisés. Les styles sont appliqués globalement à l'application.
|
||||
|
||||
## Débogage
|
||||
|
||||
### Vérifier les erreurs de format
|
||||
|
||||
Le framework valide les fichiers Markdown. En cas d'erreur :
|
||||
- Vérifier la syntaxe YAML front matter
|
||||
- Vérifier que les valeurs de `ring` et `quadrant` correspondent aux valeurs définies dans `config.json`
|
||||
- Vérifier la syntaxe Markdown
|
||||
|
||||
### Problèmes courants
|
||||
|
||||
1. **Erreur de parsing YAML** : Vérifier les guillemets et l'indentation
|
||||
2. **Blip non affiché** : Vérifier que le quadrant et le ring sont corrects
|
||||
3. **Images non chargées** : Vérifier le chemin relatif depuis `public/`
|
||||
|
||||
## Workflow recommandé
|
||||
|
||||
1. Créer une branche Git pour vos modifications
|
||||
2. Ajouter/modifier les fichiers radar
|
||||
3. Tester localement avec `npm run serve`
|
||||
4. Vérifier l'affichage et le formatage
|
||||
5. Commiter et pousser les changements
|
||||
6. Créer une pull request si applicable
|
||||
|
||||
## Commandes utiles
|
||||
|
||||
```bash
|
||||
# Installer les dépendances
|
||||
npm install
|
||||
|
||||
# Démarrer le serveur de développement
|
||||
npm run serve
|
||||
|
||||
# Build de production
|
||||
npm run build
|
||||
|
||||
# Vérifier les fichiers modifiés
|
||||
git status
|
||||
|
||||
# Voir les différences
|
||||
git diff
|
||||
```
|
||||
|
||||
## Intégration continue
|
||||
|
||||
Le projet utilise Drone CI pour l'intégration continue. Voir `.drone.yml` pour la configuration.
|
||||
|
||||
Les builds automatiques :
|
||||
- Construisent l'image Docker
|
||||
- Déploient sur l'environnement de test
|
||||
- Envoient des notifications Telegram
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
# Guide d'Utilisation du Radar Business
|
||||
|
||||
## Introduction
|
||||
|
||||
Le Radar Business est un outil stratégique pour analyser les technologies de l'écosystème Laplank/Duniter/Ğ1 sous l'angle business. Il permet d'identifier les patterns, les risques, les opportunités et de définir une stratégie d'évolution technique.
|
||||
|
||||
## Accès
|
||||
|
||||
Le Radar Business est accessible sur le **port 3004** et est protégé par un **mot de passe** : `laplank-radar`
|
||||
|
||||
L'authentification est gérée côté client via `localStorage` (session valide jusqu'à fermeture du navigateur).
|
||||
|
||||
## Structure du Radar
|
||||
|
||||
### Quadrants
|
||||
|
||||
Le radar est organisé en 4 quadrants business :
|
||||
|
||||
1. **Technologies Différenciantes** : Créent un avantage concurrentiel
|
||||
2. **Technologies de Commodité** : Nécessaires mais non différenciantes
|
||||
3. **Technologies à Risque** : Obsolètes, coûteuses, à migrer
|
||||
4. **Technologies Émergentes** : Opportunités futures
|
||||
|
||||
### Anneaux (Rings)
|
||||
|
||||
Chaque technologie est classée dans un des 4 anneaux :
|
||||
|
||||
1. **Core** : Technologies critiques pour le business model
|
||||
2. **Strategic** : Technologies stratégiques pour la croissance
|
||||
3. **Support** : Technologies de support nécessaires
|
||||
4. **Legacy** : Technologies à remplacer
|
||||
|
||||
## Métadonnées Business
|
||||
|
||||
Chaque technologie (blip) contient des métadonnées business :
|
||||
|
||||
### Métadonnées Standard
|
||||
|
||||
- **title** : Nom de la technologie
|
||||
- **ring** : Anneau (core, strategic, support, legacy)
|
||||
- **quadrant** : Quadrant business
|
||||
- **tags** : Tags pour le filtrage
|
||||
|
||||
### Métadonnées Business
|
||||
|
||||
- **businessImpact** : Impact sur le business (high, medium, low)
|
||||
- **costToReplace** : Coût estimé de remplacement en euros
|
||||
- **revenueImpact** : Impact sur les revenus (direct, indirect, none)
|
||||
- **riskLevel** : Niveau de risque (high, medium, low)
|
||||
- **maintenanceCost** : Coût annuel de maintenance en euros
|
||||
- **differentiation** : Capacité de différenciation (high, medium, low)
|
||||
|
||||
### Métadonnées Compétences
|
||||
|
||||
- **competencyLevel** : Niveau moyen de compétence (expert, intermediate, beginner)
|
||||
- **teamCoverage** : Nombre de personnes maîtrisant la technologie
|
||||
- **skillGap** : Risque de compétence manquante (high, medium, low)
|
||||
|
||||
## Pages de Stratégie
|
||||
|
||||
Le Radar Business inclut trois pages de stratégie accessibles depuis le header :
|
||||
|
||||
1. **Stratégie Technique** : Vision et roadmap technique pour l'évolution du stack
|
||||
2. **Business** : Analyse stratégique business autour de la dataviz et des flux économiques
|
||||
3. **DataViz Expert** : Opportunités supplémentaires en dataviz (Smart Cities, Green Tech, KM, Cybersecurity)
|
||||
|
||||
Ces pages sont générées dynamiquement via `public/strategie-script.js` qui convertit le contenu Markdown en HTML.
|
||||
|
||||
### Contenu des Pages
|
||||
|
||||
Le contenu des pages de stratégie est intégré directement dans `public/strategie-script.js` :
|
||||
- `strategie-evolution-technique.md` : Stratégie d'évolution technique
|
||||
- `strategie-business.md` : Analyse business et opportunités
|
||||
- `opportunites-dataviz.md` et `opportunites-dataviz-details.md` : Opportunités DataViz
|
||||
|
||||
Pour modifier le contenu, éditer directement `public/strategie-script.js` (sections `markdownContent`).
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Ajouter une Nouvelle Technologie
|
||||
|
||||
1. Créer un fichier Markdown dans `radar-business/2025-01-15/`
|
||||
2. Utiliser le format défini dans `radar-business/FORMAT-BLIP.md`
|
||||
3. Remplir toutes les métadonnées
|
||||
4. Ajouter la description et les sections recommandées
|
||||
|
||||
### Modifier une Technologie Existante
|
||||
|
||||
1. Ouvrir le fichier Markdown correspondant
|
||||
2. Modifier les métadonnées ou le contenu
|
||||
3. Mettre à jour la date si changement significatif
|
||||
|
||||
### Analyser le Radar
|
||||
|
||||
1. Exécuter le script d'analyse :
|
||||
```bash
|
||||
node scripts/analyze-business-metrics.js
|
||||
```
|
||||
2. Consulter le rapport généré dans `docs/analyse-strategique.md`
|
||||
|
||||
### Générer les Blips
|
||||
|
||||
Pour régénérer les blips depuis `technologies-duniter.md` :
|
||||
|
||||
```bash
|
||||
node scripts/extract-technologies.js
|
||||
```
|
||||
|
||||
## Interprétation des Résultats
|
||||
|
||||
### Technologies Critiques
|
||||
|
||||
Les technologies en ring "core" avec businessImpact "high" sont critiques. Elles nécessitent :
|
||||
- Maintenance proactive
|
||||
- Formation continue
|
||||
- Documentation exhaustive
|
||||
- Plans de continuité
|
||||
|
||||
### Technologies à Risque
|
||||
|
||||
Les technologies avec riskLevel "high" ou skillGap "high" présentent des risques. Actions recommandées :
|
||||
- Formation ou recrutement
|
||||
- Documentation
|
||||
- Plan de migration si nécessaire
|
||||
|
||||
### Opportunités d'Innovation
|
||||
|
||||
Les technologies émergentes avec differentiation "high" sont des opportunités. Actions :
|
||||
- POC (Proof of Concept)
|
||||
- Évaluation de l'impact
|
||||
- Adoption progressive
|
||||
|
||||
### Optimisation des Coûts
|
||||
|
||||
Les technologies de commodité avec maintenanceCost élevé peuvent être optimisées :
|
||||
- Standardisation
|
||||
- Automatisation
|
||||
- Réduction des coûts
|
||||
|
||||
## Méthodologie d'Analyse
|
||||
|
||||
### 1. Collecte des Données
|
||||
|
||||
- Inventorier toutes les technologies
|
||||
- Collecter les métadonnées business
|
||||
- Analyser les compétences de l'équipe
|
||||
|
||||
### 2. Classification
|
||||
|
||||
- Classer par quadrant business
|
||||
- Classer par ring (core, strategic, support, legacy)
|
||||
- Évaluer les métadonnées
|
||||
|
||||
### 3. Analyse
|
||||
|
||||
- Identifier les patterns
|
||||
- Calculer les métriques
|
||||
- Identifier les risques et opportunités
|
||||
|
||||
### 4. Recommandations
|
||||
|
||||
- Prioriser les actions
|
||||
- Définir la roadmap
|
||||
- Planifier les investissements
|
||||
|
||||
## Templates
|
||||
|
||||
### Template de Blip
|
||||
|
||||
Voir `radar-business/FORMAT-BLIP.md` pour le template complet.
|
||||
|
||||
### Template d'Analyse
|
||||
|
||||
Le script `analyze-business-metrics.js` génère automatiquement un rapport d'analyse.
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Mise à Jour Régulière
|
||||
|
||||
- Mettre à jour les métadonnées trimestriellement
|
||||
- Réviser les classifications annuellement
|
||||
- Mettre à jour les coûts et risques
|
||||
|
||||
### Révision Stratégique
|
||||
|
||||
- Révision annuelle de la stratégie
|
||||
- Ajustement des priorités
|
||||
- Mise à jour de la roadmap
|
||||
|
||||
## Déploiement
|
||||
|
||||
Le Radar Business est déployé via Docker et Portainer :
|
||||
|
||||
- **Dockerfile** : `Dockerfile.business`
|
||||
- **Docker Compose** : `docker-compose.business.yml`
|
||||
- **Port** : 3004 (mappé depuis le port 3000 du conteneur)
|
||||
- **Base path** : `/` (racine, pas de sous-chemin)
|
||||
|
||||
Voir [deploiement.md](./deploiement.md) pour les détails complets.
|
||||
|
||||
## Ressources
|
||||
|
||||
- **Format des blips** : `radar-business/FORMAT-BLIP.md`
|
||||
- **Configuration** : `radar-business/config-business.json`
|
||||
- **Script de stratégie** : `public/strategie-script.js`
|
||||
- **Analyse stratégique** : `docs/analyse-strategique.md`
|
||||
- **Stratégie d'évolution** : `docs/strategie-evolution-technique.md`
|
||||
- **Stratégie business** : `docs/strategie-business.md`
|
||||
- **Opportunités DataViz** : `docs/opportunites-dataviz.md` et `docs/opportunites-dataviz-details.md`
|
||||
|
||||
## Support
|
||||
|
||||
Pour toute question ou contribution, consulter la documentation ou contacter l'équipe technique.
|
||||
|
||||
609
export/team.md
Normal file
609
export/team.md
Normal file
@@ -0,0 +1,609 @@
|
||||
# Profils de l'équipe
|
||||
|
||||
Ce document contient tous les profils des membres de l'équipe fusionnés.
|
||||
|
||||
---
|
||||
|
||||
## 1000i100
|
||||
|
||||
---
|
||||
name: "1000i100"
|
||||
fullName: "1000i100"
|
||||
role: "DevOps & Développeur Web"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 10
|
||||
joinDate: "2018-01"
|
||||
interests: ["Serverless", "CI/CD", "Docker", "Photographie", "CNV", "Modèles économiques"]
|
||||
skills:
|
||||
- name: "Serverless"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "GitLab"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "CI/CD"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "Docker"
|
||||
level: expert
|
||||
years: 7
|
||||
lastUsed: "2024-12"
|
||||
- name: "web"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Polyvalence"
|
||||
- "Photographie"
|
||||
- "Soutien psychologique"
|
||||
- "CNV (Communication Non Violente)"
|
||||
projects:
|
||||
- "Outils serverless"
|
||||
- "Pipeline GitLab CI/CD"
|
||||
---
|
||||
|
||||
Développeur d'outils serverless, et plombier des pipeline Gitlab (CI/CD avec Docker). Enfin une monnaie mécaniquement redistributive ! Avec un soupçon de revenu de base, une bonne dose d'auto-gestion et une communauté adorable ! Informaticien couteau suisse à dominante développeur web, photographe à ses heures, soutien psy informel, amateur de CNV et de modèles économiques expérimental et éthique !
|
||||
|
||||
---
|
||||
|
||||
## aya
|
||||
|
||||
---
|
||||
name: "aya"
|
||||
fullName: "aya"
|
||||
role: "Administrateur Système & Infrastructure Distribuée"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 23
|
||||
joinDate: "2021-01"
|
||||
interests: ["Logiciels libres", "Infrastructure distribuée", "Stockage distribué", "IPFS", "ThreeFold"]
|
||||
skills:
|
||||
- name: "Linux"
|
||||
level: expert
|
||||
years: 23
|
||||
lastUsed: "2024-12"
|
||||
- name: "glusterfs"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2023-06"
|
||||
- name: "cephfs"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2023-06"
|
||||
- name: "ipfs"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "infrastructure"
|
||||
level: expert
|
||||
years: 15
|
||||
lastUsed: "2024-12"
|
||||
- name: "systèmes distribués"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
- name: "ThreeFold"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Vulgarisation"
|
||||
- "Autonomie"
|
||||
- "Recherche"
|
||||
projects:
|
||||
- "Infrastructure d'hébergement distribué"
|
||||
---
|
||||
|
||||
Je participe à la vulgarisation des logiciels libres depuis ma première installation de linux debian potato en 2001.
|
||||
|
||||
J'ai découvert la monnaie libre à travers mes recherches concernant les systèmes de fichiers. Travaillant principalement sur des infrastructures d'hébergement distribué, j'ai utilisé différents systèmes de réplication de fichiers comme glusterfs, cephfs, pour en arriver à ipfs. C'est en cherchant une alternative à filecoin, la crypto proposée par ipfs pour mettre en commun son espace de stockage, que je découvre la monnaie libre, on est en 2021.
|
||||
|
||||
Je rejoins Axiom-Team pour participer à la vulgarisation de la monnaie libre.
|
||||
|
||||
---
|
||||
|
||||
## boris
|
||||
|
||||
---
|
||||
name: "boris"
|
||||
fullName: "boris"
|
||||
role: "UX/UI Designer & Développeur Full Stack"
|
||||
availability: 40
|
||||
seniorityLevel: intermediate
|
||||
yearsExperience: 8
|
||||
joinDate: "2018-01"
|
||||
interests: ["UX/UI", "LLM", "Langues étrangères", "Médecine traditionnelle chinoise", "Feng Shui", "Tao", "Musique"]
|
||||
skills:
|
||||
- name: "UX"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "UI"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Figma"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "LLM"
|
||||
level: intermediate
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
- name: "JavaScript"
|
||||
level: intermediate
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "TypeScript"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "APIs"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Vis.js"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Polyvalence"
|
||||
- "Créativité"
|
||||
- "Curiosité"
|
||||
- "Multiculturalisme"
|
||||
projects:
|
||||
- "UX/UI de Ğecko (Figma)"
|
||||
- "App de médecine chinoise basée sur LLM"
|
||||
- "Site monnaie-libre.fr"
|
||||
- "Duniter | Accueil"
|
||||
- "cesium.app"
|
||||
- "Ğ1Quest (vue radar des annonces Ğchange)"
|
||||
- "Ğrocéliande (skin Ğchange style Amazon)"
|
||||
- "g1.business (routes commerciales)"
|
||||
- "Ğ1Gate (flux de monnaie en treemap)"
|
||||
- "H2G2 (guide du terraformeur terrien)"
|
||||
- "Ğ1 KDE Notifier"
|
||||
- "Simulateur RSA / Prime d'activité"
|
||||
- "Cerveau externe (Vis.js pour impros rap)"
|
||||
- "NoBS Troll-Emploi (moteur de recherche d'emploi)"
|
||||
---
|
||||
|
||||
Il est assez dispersé, "jack of all trade, master of none". Ces derniers temps, il passe beaucoup de temps à faire de la génération de musiques rigolotes (ou autre) avec les LLM et Suno. Il aime les langues étrangères (l'anglais surtout), la médecine traditionnelle chinoise, le Feng Shui (le tao en général). Il est communiste. Il a bossé sur l'UX/UI de Ğecko (via Figma). Grâce à Cursor, il développe une app de médecine chinoise basée sur les LLM. Dans la Ğ1, il a essayé de contribuer à l'onboarding (il a refait le site monnaie-libre.fr, Duniter | Accueil, et fait le site cesium.app). Il a aussi fait des clients Ğchange : Ğ1Quest (une projection des annonces Ğchange, notamment en "vue radar"), Ğrocéliande (un genre de skin pour Ğchange calqué sur l'interface d'Amazon, et qui ne prend que les annonces avec "envoi possible" dans la description), g1.business (qui permet de repérer les "routes commerciale", de faire correspondre pour un produit l'offre d'un endroit et la demande à un endroit distant, et qui projette sur une carte les moyens de productions disponibles à la location en Ğ1). Il a aussi fait Ğ1Gate (qui permet de suivre les flux de monnaie en vue "treemap"), H2G2 "le guide du terraformeur terrien" (une vue à la recette MineCraft de choses qu'on peut produire "dans la vraie vie"), Ğ1 KDE Notifier (Un petit outil pour être notifié de mouvements sur un portefeuille Ğ1), un Simulateur RSA / Prime d'activité (Un simulateur RSA/prime d'activité plus très à jour au niveau des données, mais qui permet de se rendre compte à quel point le fonctionnement de la prime d'activité est complètement stupide, et incite à éviter de travailler de façon trop importante trop ponctuellement, si on ne veut pas risquer de perdre de l'argent en allant se casser le cul au boulot), Cerveau externe (Un truc fait avec Vis.js, pour projeter des mots, colorés suivant la rime, regroupés autour des consonnes, et liés s'ils appartiennent à un même thème. Dans l'idée de faire des impros de rap avec. Proto sans réelle interface utilisateur utilisable par les moldus. Faire F5 pour raffraîchir et ainsi avoir un autre graphe de mots.), NoBS Troll-Emploi (Un moteur de recherche d'emploi basé sur l'API Pôle-Emploi et qui permet d'avoir plus de filtres : mots-clefs à exclure, pas de tutoiement, pas de "digital", etc… Idéal pour les gens qui, certes, acceptent d'être exploités lorsqu'ils développent du logiciel, mais veulent diminuer au maximum la quantité de bullshit dans leur job).
|
||||
|
||||
---
|
||||
|
||||
## elois
|
||||
|
||||
---
|
||||
name: "elois"
|
||||
fullName: "Eloïs"
|
||||
role: "Développeur Blockchain"
|
||||
availability: 25
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 5
|
||||
joinDate: "2019-01"
|
||||
interests: ["Blockchain", "Rust", "Migration", "Cryptographie"]
|
||||
skills:
|
||||
- name: "Rust"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "blockchain"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "Substrate"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "migration"
|
||||
level: expert
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Autodidactie"
|
||||
- "Recherche"
|
||||
- "Architecture"
|
||||
projects:
|
||||
- "Rustification de Duniter v1"
|
||||
- "Duniter v2S"
|
||||
---
|
||||
|
||||
A appris les technologies blockchain en autodidact, travaillé sur la "rustification" (passage en Rust) de Duniter v1, puis bossé chez MoonPay.
|
||||
|
||||
---
|
||||
|
||||
## fred
|
||||
|
||||
---
|
||||
name: "fred"
|
||||
fullName: "Fred"
|
||||
role: "Développeur & Architecte Systèmes Décentralisés"
|
||||
availability: 40
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 20
|
||||
joinDate: "2014-01"
|
||||
interests: ["IPFS", "Secure ScuttleButt", "Nostr", "TiddlyWiki", "ThreeFold", "Systèmes décentralisés"]
|
||||
skills:
|
||||
- name: "IPFS"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "Secure ScuttleButt"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
- name: "Nostr"
|
||||
level: expert
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "TiddlyWiki"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "développement"
|
||||
level: expert
|
||||
years: 20
|
||||
lastUsed: "2024-12"
|
||||
- name: "ThreeFold"
|
||||
level: intermediate
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Architecture"
|
||||
- "Entrepreneuriat"
|
||||
- "Innovation"
|
||||
projects:
|
||||
- "Astroport (système d'information combinant Ğ1, IPFS et Nostr)"
|
||||
- "G1SMS (système de paiement par SMS en Ğ1)"
|
||||
- "G1billet (paper wallet pour la Ğ1)"
|
||||
- "Linkeo (entreprise)"
|
||||
---
|
||||
|
||||
A monté une boite (Linkeo) qui a bouffé une partie du marché de PagesJaunes début/milieu des années 2000. Très intéressé (et sachant) sur IPFS, Secure ScuttleButt, Nostr et TiddlyWiki. Il développe Astroport, un système d'information qui combine la Ğ1, IPFS et Nostr. Par le passé, il a aussi créé G1SMS (système de paiement par SMS en Ğ1) et G1billet (paper wallet pour la Ğ1).
|
||||
|
||||
---
|
||||
|
||||
## hugo
|
||||
|
||||
---
|
||||
name: "hugo"
|
||||
fullName: "Hugo Trentesaux"
|
||||
role: "Financement & Gestion"
|
||||
availability: 20
|
||||
seniorityLevel: intermediate
|
||||
yearsExperience: 5
|
||||
joinDate: "2017-01"
|
||||
interests: ["Financement", "Gestion", "Rédaction", "Administration"]
|
||||
skills:
|
||||
- name: "financement"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "rédaction"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "gestion"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Rédaction"
|
||||
- "Administration"
|
||||
- "Gestion de projet"
|
||||
projects:
|
||||
- "Dossier de financement Ğecko (ADEME)"
|
||||
---
|
||||
|
||||
Je m'intéresse à la Ğ1 depuis 2017 et pense que l'association Axiom Team constitue une base juridique utile car nécessaire pour de nombreuses interactions avec le monde €.
|
||||
|
||||
J'ai travaillé sur le dossier de financement de Ǧecko auprès de l'ADEME avec succès. À l'avenir, je compte participer au fonctionnement d'Axiom Team, et à la partie rédactionnelle des dossiers de financement.
|
||||
|
||||
---
|
||||
|
||||
## manuTopik
|
||||
|
||||
---
|
||||
name: "manuTopik"
|
||||
fullName: "ManUtopiK"
|
||||
role: "Développeur Web Full Stack"
|
||||
availability: 40
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 12
|
||||
joinDate: "2014-01"
|
||||
interests: ["Web", "Alternatives", "Monnaie libre", "Solarpunk", "Intelligence collective"]
|
||||
skills:
|
||||
- name: "VueJS"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "Nuxt.js"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-11"
|
||||
- name: "JavaScript"
|
||||
level: expert
|
||||
years: 12
|
||||
lastUsed: "2024-12"
|
||||
- name: "TypeScript"
|
||||
level: intermediate
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "CMS"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "web"
|
||||
level: expert
|
||||
years: 12
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Communication"
|
||||
- "Vulgarisation"
|
||||
- "Créativité"
|
||||
projects:
|
||||
- "monnaie-libre.fr"
|
||||
- "carte.monnaie-libre.fr"
|
||||
- "Doc silkaj"
|
||||
- "WotWizard-UI"
|
||||
- "g1lib"
|
||||
- "Duniter UI (nuxt - abandonné)"
|
||||
- "Extension web g1Compagnon (en cours)"
|
||||
- "Interface web pour g1Billet (en cours)"
|
||||
---
|
||||
|
||||
Diplomé dans le domaine des énergies renouvelables, mon côté "web enthousiaste" m'a finalement amené à faire du développement web depuis + de 12 ans.
|
||||
|
||||
Passionné par tout ce qui est "alternatif" et qui rend libre, j'ai découvert le concept de la monnaie libre en 2014. L'économie actuelle est à mes yeux le principal facteur du bordel que l'on a mis sur cette planète depuis des générations. J'espère en un monde un peu plus libre, auto gouverné en intelligence collective, et avec du #solarpunk comme horizon. Profitons des crises pour tout changer !
|
||||
|
||||
À fond sur VueJS ; il a créé un CMS basé sur VueJS.
|
||||
|
||||
## Contributions
|
||||
|
||||
- Développement et rédaction du site monnaie-libre.fr (Dépôt du site, de l'api)
|
||||
- Développement de la carte.monnaie-libre.fr (Dépôt)
|
||||
- Doc silkaj
|
||||
- WotWizard-UI
|
||||
- g1lib
|
||||
- Duniter UI avec nuxt (Abandonné)
|
||||
|
||||
## En cours
|
||||
|
||||
- Extension web g1Compagnon
|
||||
- Interface web pour g1Billet
|
||||
|
||||
---
|
||||
|
||||
## poka
|
||||
|
||||
---
|
||||
name: "poka"
|
||||
fullName: "Poka"
|
||||
role: "Développeur Full Stack & Administrateur Système"
|
||||
availability: 50
|
||||
seniorityLevel: expert
|
||||
yearsExperience: 8
|
||||
joinDate: "2016-01"
|
||||
interests: ["Mobile", "Infrastructure", "Automatisation", "Blockchain"]
|
||||
skills:
|
||||
- name: "Flutter"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Dart"
|
||||
level: expert
|
||||
years: 4
|
||||
lastUsed: "2024-12"
|
||||
- name: "Python"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-11"
|
||||
- name: "bash"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
- name: "ProxMox"
|
||||
level: expert
|
||||
years: 6
|
||||
lastUsed: "2024-12"
|
||||
- name: "infrastructure"
|
||||
level: expert
|
||||
years: 8
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Autonomie"
|
||||
- "Pédagogie"
|
||||
- "Maintenance système"
|
||||
projects:
|
||||
- "Ğecko"
|
||||
- "Ğ1-stats"
|
||||
- "jaklis"
|
||||
- "py-g1-migrator"
|
||||
- "Infrastructure Axiom-Team"
|
||||
---
|
||||
|
||||
Je suis contributeur actif sur le projet Duniter depuis 2016 aux RML7 de Laval.
|
||||
|
||||
Je code Ğecko en Flutter/Dart. Je maintiens aussi l'infra Axiom-Team, soit 2 serveurs ProxMox.
|
||||
|
||||
J'ai aussi codé Ğ1-stats en bash. Et jaklis en python. J'ai aussi codé py-g1-migrator
|
||||
|
||||
---
|
||||
|
||||
## syoul
|
||||
|
||||
---
|
||||
name: "syoul"
|
||||
fullName: "Syoul"
|
||||
role: "Etudiant IPSSI - Alternance Admin Infrastructure Securisee chez AJR"
|
||||
availability: 50
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 1
|
||||
joinDate: "2024-06"
|
||||
interests: ["Autohebergement", "Proxmox", "Docker", "Infrastructure", "Securite"]
|
||||
skills:
|
||||
- name: "Proxmox"
|
||||
level: beginner
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "Docker"
|
||||
level: beginner
|
||||
years: 1
|
||||
lastUsed: "2024-12"
|
||||
- name: "Linux"
|
||||
level: beginner
|
||||
years: 1
|
||||
lastUsed: "2024-12"
|
||||
- name: "autohebergement"
|
||||
level: beginner
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Apprentissage"
|
||||
- "Curiosite"
|
||||
- "Autonomie"
|
||||
projects:
|
||||
- "Autohebergement personnel (Proxmox + Docker)"
|
||||
- "Alternance AJR - Administration Infrastructure"
|
||||
---
|
||||
|
||||
Etudiant a l'IPSSI depuis 6 mois, en alternance Administrateur Infrastructure Securisee chez AJR.
|
||||
|
||||
Gere son infrastructure personnelle avec Proxmox et Docker pour l'autohebergement de services.
|
||||
|
||||
---
|
||||
|
||||
## tuxmain
|
||||
|
||||
---
|
||||
name: "tuxmain"
|
||||
fullName: "tuxmain"
|
||||
role: "Étudiant Math & Cryptographie"
|
||||
availability: 20
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 3
|
||||
joinDate: "2022-01"
|
||||
interests: ["Mathématiques", "Cryptographie", "Chiffrage", "Électronique", "Minetest"]
|
||||
skills:
|
||||
- name: "cryptographie"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "chiffrage"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-12"
|
||||
- name: "math"
|
||||
level: expert
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
- name: "électronique"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Recherche"
|
||||
- "Analyse"
|
||||
- "Bidouille"
|
||||
projects:
|
||||
- "Administration serveur Minetest"
|
||||
- "Bidouille électronique"
|
||||
---
|
||||
|
||||
Étudiant en math. Bien compétent sur la cryptographie, le chiffrage, les conversions de clef d'une base en une autre. Administrateur de serveur Minetest. Il bidouille aussi de l'électronique.
|
||||
|
||||
---
|
||||
|
||||
## vivien
|
||||
|
||||
---
|
||||
name: "vivien"
|
||||
fullName: "Vivien"
|
||||
role: "Développeur"
|
||||
availability: 40
|
||||
seniorityLevel: beginner
|
||||
yearsExperience: 2
|
||||
joinDate: "2023-01"
|
||||
interests: ["Cesium", "Godot", "Jeux", "Cartes Magic"]
|
||||
skills:
|
||||
- name: "Cesium"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-12"
|
||||
- name: "Godot"
|
||||
level: beginner
|
||||
years: 2
|
||||
lastUsed: "2024-11"
|
||||
softSkills:
|
||||
- "Apprentissage"
|
||||
- "Curiosité"
|
||||
projects:
|
||||
- "Contribution à Cesium"
|
||||
- "Développement en Godot"
|
||||
---
|
||||
|
||||
Se forme pour contribuer à certains logiciels de la Ğ1 (Cesium). Développe aussi en Godot. Passionné de jeux (cartes Magic notamment).
|
||||
|
||||
---
|
||||
|
||||
## yvv
|
||||
|
||||
---
|
||||
name: "yvv"
|
||||
fullName: "Yvv"
|
||||
role: "Gestion & Mobilisation"
|
||||
availability: 70
|
||||
seniorityLevel: senior
|
||||
yearsExperience: 10
|
||||
joinDate: "2015-01"
|
||||
interests: ["Gestion", "Mobilisation", "Économie du don", "Wiki", "Médiathèque"]
|
||||
skills:
|
||||
- name: "gestion"
|
||||
level: expert
|
||||
years: 10
|
||||
lastUsed: "2024-12"
|
||||
- name: "médiathèque"
|
||||
level: intermediate
|
||||
years: 3
|
||||
lastUsed: "2024-11"
|
||||
- name: "wiki"
|
||||
level: intermediate
|
||||
years: 5
|
||||
lastUsed: "2024-12"
|
||||
softSkills:
|
||||
- "Gestion"
|
||||
- "Organisation"
|
||||
- "Mobilisation"
|
||||
- "Communication"
|
||||
projects:
|
||||
- "Tuyauterie autogestion des dons (UNL)"
|
||||
- "WishBounty v2"
|
||||
- "FAQs version wiki"
|
||||
- "Médiathèque (nocodb)"
|
||||
- "Librodrome"
|
||||
- "Conserverie éphémère mobile"
|
||||
---
|
||||
|
||||
Vieux bouc dans le CA, je tire ma révérence en tant que secrétaire. Focus sur ce qui m'intéresse le plus, nouvelle forme de mobilisation.
|
||||
|
||||
## Pour mission UNL
|
||||
|
||||
- Aboutir la tuyauterie autogestion des dons.
|
||||
- L'élargir pour une v2 sur … un goût de paradis, le WishBounty.
|
||||
|
||||
## Pour mission fédération - services aux monnaie-libristes
|
||||
|
||||
- Bosser sur une FAQs version wiki, si un mediawiki ou autre voit le jour.
|
||||
- Bosser sur une médiathèque, si un nocodb ou autre voit le jour.
|
||||
|
||||
## Pour ML
|
||||
|
||||
- Diffuser mon bouquin "une économie du don - enfin concevable" et m'en servir de support pour mener des ateliers éco et "passer la seconde".
|
||||
- Lancer un événement structurant, le Librodrome.
|
||||
- Lancer une expérience de production collective monnaie-libriste, probablement une conserverie éphémère mobile.
|
||||
|
||||
445
export/technologies-team.md
Normal file
445
export/technologies-team.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# Technologies et Compétences - Écosystème Duniter/Ğ1
|
||||
|
||||
Ce document liste les technologies et compétences identifiées dans l'écosystème Duniter/Ğ1 basé sur l'analyse de https://git.duniter.org/
|
||||
|
||||
## Technologies de Développement
|
||||
|
||||
### Langages de Programmation
|
||||
|
||||
#### Rust
|
||||
- **Utilisation** : Développement du nœud Duniter v2S (basé sur Substrate)
|
||||
- **Projets** :
|
||||
- `Duniter v2S` : Nœud blockchain principal
|
||||
- `Ğcli-v2s` : Interface en ligne de commande Rust
|
||||
- `homebrew-duniter-gcli` : Package Homebrew pour Ğcli
|
||||
- **Compétences requises** : Rust avancé, développement blockchain, Substrate framework
|
||||
|
||||
#### Python
|
||||
- **Utilisation** : Clients en ligne de commande et outils
|
||||
- **Projets** :
|
||||
- `silkaj` : Client CLI Python pour la monnaie Ğ1
|
||||
- `Tikka` : Client riche pour la monnaie Ğ1
|
||||
- **Compétences requises** : Python, développement CLI, APIs REST
|
||||
|
||||
#### JavaScript/TypeScript
|
||||
- **Utilisation** : Clients web, extensions navigateur, sites web
|
||||
- **Projets** :
|
||||
- `Ğ1Companion` : Extension web pour navigateurs
|
||||
- Clients web divers
|
||||
- **Compétences requises** : JavaScript/TypeScript, développement d'extensions navigateur, Web APIs
|
||||
|
||||
### Frameworks et Bibliothèques
|
||||
|
||||
#### Substrate Framework
|
||||
- **Utilisation** : Framework blockchain pour Duniter v2S
|
||||
- **Description** : Framework Rust pour construire des blockchains personnalisées
|
||||
- **Compétences requises** : Blockchain, Rust, Substrate, consensus algorithms
|
||||
|
||||
#### Nuxt.js
|
||||
- **Utilisation** : Framework Vue.js pour sites web
|
||||
- **Projets** :
|
||||
- `monnaie-libre-fr` : Site web avec Nuxt + nuxt-content
|
||||
- **Compétences requises** : Vue.js, Nuxt.js, SSR, JAMstack
|
||||
|
||||
#### NetlifyCMS
|
||||
- **Utilisation** : CMS headless basé sur Git
|
||||
- **Projets** :
|
||||
- `monnaie-libre-fr` : CMS pour le site web
|
||||
- **Compétences requises** : Git-based CMS, JAMstack, workflows Git
|
||||
|
||||
#### WordUp CMS
|
||||
- **Utilisation** : CMS pour sites web
|
||||
- **Projets** :
|
||||
- `axiom-team-fr` : Site de production avec WordUp
|
||||
- **Compétences requises** : CMS management, intégration d'APIs
|
||||
|
||||
### Outils et Bibliothèques Spécialisées
|
||||
|
||||
#### Squid (Indexer)
|
||||
- **Utilisation** : Indexation de données blockchain
|
||||
- **Projets** :
|
||||
- `duniter-squid` : Indexer basé sur Squid pour Duniter v2S
|
||||
- **Compétences requises** : Indexation de données, GraphQL, blockchain data processing
|
||||
|
||||
#### g1-papi
|
||||
- **Utilisation** : Bibliothèque API pour Ğ1
|
||||
- **Type** : Bibliothèque partagée
|
||||
- **Compétences requises** : API design, développement de bibliothèques
|
||||
|
||||
### Clients et Interfaces
|
||||
|
||||
#### Clients CLI (Command Line Interface)
|
||||
- **Rust CLI** : `Ğcli-v2s` - Interface avancée pour utilisateurs experts
|
||||
- **Python CLI** : `silkaj`, `Tikka` - Clients en ligne de commande
|
||||
- **Compétences requises** : Développement CLI, UX en ligne de commande, parsing d'arguments
|
||||
|
||||
#### Extensions Navigateur
|
||||
- **Ğ1Companion** : Extension web pour navigateurs
|
||||
- **Compétences requises** : Web Extensions API, Chrome/Firefox extensions, JavaScript
|
||||
|
||||
#### Clients Graphiques
|
||||
- **Ğecko** : Client avec interface graphique
|
||||
- **Cesium-grp/cesium2s** : Client Cesium pour Duniter v2s
|
||||
- **Compétences requises** : Développement d'interfaces graphiques, frameworks UI
|
||||
|
||||
### Intégrations et APIs
|
||||
|
||||
#### Intégrations Externes
|
||||
- **HelloAsso** : Intégration pour dons
|
||||
- **Paheko** : Intégration comptable
|
||||
- **ĞWishBounty** : Application pour automatiser les flux de dons
|
||||
- **Compétences requises** : Intégration d'APIs tierces, webhooks, synchronisation de données
|
||||
|
||||
#### APIs Internes
|
||||
- **api-axiom-team-fr** : API pour le site Axiom
|
||||
- **Compétences requises** : REST APIs, GraphQL, documentation d'API
|
||||
|
||||
## Technologies d'Authentification et d'Identité
|
||||
|
||||
### Authentification et Autorisation
|
||||
|
||||
#### Microsoft Entra (concurrents)
|
||||
- **Utilisation** : Solution d'identité et d'accès cloud de Microsoft
|
||||
- **Description** : Plateforme d'identité en tant que service (IDaaS) qui fournit l'authentification unique (SSO), la gestion des identités et l'accès conditionnel. Alternative aux solutions d'authentification traditionnelles.
|
||||
- **Compétences requises** : Gestion d'identité cloud, SSO, intégration d'identité, sécurité des accès
|
||||
|
||||
#### AUTHZ et AUTHN
|
||||
- **Utilisation** : Concepts fondamentaux de sécurité
|
||||
- **Description** :
|
||||
- **AUTHN (Authentication)** : Vérification de l'identité d'un utilisateur (qui êtes-vous ?)
|
||||
- **AUTHZ (Authorization)** : Vérification des permissions d'accès (que pouvez-vous faire ?)
|
||||
- **Compétences requises** : Principes de sécurité, gestion des identités, contrôle d'accès, modèles de permissions
|
||||
|
||||
#### Better Auth
|
||||
- **Utilisation** : Bibliothèque d'authentification moderne
|
||||
- **Description** : Solution d'authentification open-source offrant une API simple et flexible pour gérer l'authentification dans les applications web. Supporte OAuth, email/password, et autres méthodes d'authentification.
|
||||
- **Compétences requises** : Développement web, authentification, OAuth, sécurité des applications
|
||||
|
||||
### Identité Décentralisée
|
||||
|
||||
#### DID et UCAN
|
||||
- **Utilisation** : Identifiants décentralisés et système d'autorisation
|
||||
- **Description** :
|
||||
- **DID (Decentralized Identifiers)** : Identifiants uniques décentralisés qui permettent aux utilisateurs de contrôler leur identité sans dépendre d'une autorité centrale
|
||||
- **UCAN (User Controlled Authorization Networks)** : Système d'autorisation basé sur des capacités (capabilities) où les utilisateurs contrôlent leurs propres permissions
|
||||
- **Compétences requises** : Identité décentralisée, Web3, cryptographie, systèmes d'autorisation basés sur les capacités
|
||||
|
||||
#### VC (Verifiable Credentials)
|
||||
- **Utilisation** : Credentials vérifiables pour l'identité numérique
|
||||
- **Description** : Standard W3C pour les credentials numériques qui peuvent être vérifiés cryptographiquement. Permet de créer des identités numériques portables et vérifiables sans dépendre d'une autorité centrale.
|
||||
- **Compétences requises** : Standards W3C, identité numérique, cryptographie, vérification de credentials, blockchain (optionnel)
|
||||
|
||||
### Protocoles d'Authentification
|
||||
|
||||
#### OpenID Connect
|
||||
- **Utilisation** : Protocole d'authentification et d'autorisation
|
||||
- **Description** : Couche d'identité construite sur OAuth 2.0 qui permet aux clients de vérifier l'identité d'un utilisateur basée sur l'authentification effectuée par un serveur d'autorisation. Standard de l'industrie pour l'authentification fédérée.
|
||||
- **Compétences requises** : OAuth 2.0, protocoles d'authentification, intégration SSO, sécurité web
|
||||
|
||||
#### SPIFFE
|
||||
- **Utilisation** : Identité sécurisée pour les workloads en production
|
||||
- **Description** : SPIFFE (Secure Production Identity Framework For Everyone) fournit un cadre pour identifier et authentifier les workloads dans des environnements hétérogènes et distribués. Utilise des identités basées sur des certificats X.509 ou JWT.
|
||||
- **Compétences requises** : Sécurité des microservices, identité des workloads, mTLS, infrastructure distribuée, Kubernetes, service mesh
|
||||
|
||||
## Technologies d'Infrastructure Décentralisée
|
||||
|
||||
### ThreeFold
|
||||
|
||||
#### Zero OS
|
||||
- **Utilisation** : Système d'exploitation autonome pour infrastructure décentralisée
|
||||
- **Description** : Système d'exploitation efficace et sécurisé qui s'exécute directement sur le matériel, permettant un cloud autonome
|
||||
- **Compétences requises** : Administration système bare metal, cloud décentralisé, Zero OS
|
||||
|
||||
#### ThreeFold Grid
|
||||
- **Utilisation** : Infrastructure Internet décentralisée globale
|
||||
- **Description** : Plateforme opérationnelle d'infrastructure Internet décentralisée déployée localement, scalable globalement, possédée et alimentée par les utilisateurs
|
||||
- **Compétences requises** : Infrastructure décentralisée, cloud computing, réseaux distribués
|
||||
|
||||
#### 3Node
|
||||
- **Utilisation** : Nœuds physiques de l'infrastructure ThreeFold
|
||||
- **Description** : Serveurs informatiques dédiés à 100% au réseau, fournissant capacité de calcul, stockage et réseau
|
||||
- **Compétences requises** : Administration de serveurs, déploiement de nœuds, maintenance hardware
|
||||
|
||||
#### ThreeFold Compute
|
||||
- **Utilisation** : Capacité de calcul bare metal
|
||||
- **Description** : Peut exécuter toute charge de travail Web2, Web3 ou IA à la périphérie d'Internet, avec plus de scalabilité et de fiabilité
|
||||
- **Compétences requises** : Virtualisation, conteneurisation, Kubernetes, edge computing
|
||||
|
||||
#### ThreeFold Data Storage
|
||||
- **Utilisation** : Système de stockage de données inviolable
|
||||
- **Description** : Les données ne peuvent pas être compromises et restent toujours privées, possédées par vous. Système scalable jusqu'au niveau planétaire, au moins 10x plus efficace et plusieurs ordres de grandeur plus sécurisé et fiable
|
||||
- **Compétences requises** : Stockage distribué, réplication de données, sécurité des données
|
||||
|
||||
#### ThreeFold Network (Mycelium)
|
||||
- **Utilisation** : Réseau overlay chiffré de bout en bout
|
||||
- **Description** : Réseau toujours à la recherche du chemin le plus court possible entre les participants. Adresse Internet logique liée de manière sécurisée à une clé privée. Scalabilité illimitée et optimisations de performance
|
||||
- **Compétences requises** : Réseaux overlay, chiffrement de bout en bout, routage réseau
|
||||
|
||||
#### ThreeFold Blockchain
|
||||
- **Utilisation** : Blockchain pour la vérification et l'enregistrement de la capacité
|
||||
- **Description** : Vérifie, enregistre et sécurise la capacité des nœuds sur la blockchain ThreeFold
|
||||
- **Compétences requises** : Blockchain, consensus, cryptographie
|
||||
|
||||
#### ThreeFold Cloud
|
||||
- **Utilisation** : Cloud open-source décentralisé
|
||||
- **Description** : Déploiement de machines virtuelles, conteneurs, clusters Kubernetes, web gateways et plus sur un cloud open source décentralisé best-effort
|
||||
- **Compétences requises** : Cloud computing, Kubernetes, déploiement d'applications, administration système
|
||||
|
||||
#### AIBox
|
||||
- **Utilisation** : Solution de calcul IA auto-hébergée alimentée par ThreeFold
|
||||
- **Description** : Solution de calcul IA dédiée fonctionnant sur l'infrastructure ThreeFold
|
||||
- **Compétences requises** : Intelligence artificielle, machine learning, infrastructure IA
|
||||
|
||||
#### 3Phone
|
||||
- **Utilisation** : Appareils sécurisés de la famille 3Phone
|
||||
- **Description** : Premiers appareils sécurisés conçus pour fonctionner de manière transparente avec le ThreeFold Grid
|
||||
- **Compétences requises** : Développement mobile, sécurité des appareils, intégration réseau
|
||||
|
||||
#### 3Router
|
||||
- **Utilisation** : Routeurs intelligents pour connexions optimisées
|
||||
- **Description** : Routeurs intelligents garantissant des connexions de chemin le plus court entre nœuds et téléphones avec chiffrement de bout en bout
|
||||
- **Compétences requises** : Routage réseau, optimisation de réseau, sécurité réseau
|
||||
|
||||
## Technologies d'Infrastructure et Déploiement
|
||||
|
||||
### Conteneurisation
|
||||
- **Docker** : Conteneurisation des applications
|
||||
- **Compétences requises** : Docker, Docker Compose, orchestration de conteneurs
|
||||
|
||||
### Déploiement Web
|
||||
- **Netlify** : Déploiement JAMstack (mentionné pour monnaie-libre-fr)
|
||||
- **Compétences requises** : CI/CD, déploiement continu, Netlify
|
||||
|
||||
### Gestion de Code Source
|
||||
- **Git** : Système de contrôle de version
|
||||
- **Forge Git** : git.duniter.org (forge Git auto-hébergée)
|
||||
- **Compétences requises** : Git avancé, workflows Git, gestion de forge
|
||||
|
||||
### Package Management
|
||||
- **Homebrew** : Gestion de paquets pour macOS
|
||||
- **npm/yarn** : Gestion de paquets JavaScript
|
||||
- **pip/poetry** : Gestion de paquets Python
|
||||
- **Cargo** : Gestion de paquets Rust
|
||||
- **Compétences requises** : Gestion de dépendances, gestion de versions, publication de paquets
|
||||
|
||||
## Compétences d'Administration Système
|
||||
|
||||
### Administration Linux/Unix
|
||||
- **Systèmes d'exploitation** : Linux (Debian, Ubuntu, etc.)
|
||||
- **Compétences requises** :
|
||||
- Administration système Linux
|
||||
- Gestion des utilisateurs et permissions
|
||||
- Configuration réseau
|
||||
- Monitoring système
|
||||
- Gestion des logs
|
||||
- Sécurisation des serveurs
|
||||
|
||||
### Administration Blockchain
|
||||
- **Gestion de nœuds** : Administration de nœuds Duniter
|
||||
- **Compétences requises** :
|
||||
- Configuration de nœuds blockchain
|
||||
- Gestion de la synchronisation
|
||||
- Monitoring de la blockchain
|
||||
- Gestion des clés cryptographiques
|
||||
- Maintenance des nœuds
|
||||
|
||||
### Bases de Données
|
||||
- **PostgreSQL** : Base de données relationnelle utilisée dans les projets
|
||||
- **Compétences requises** :
|
||||
- Administration PostgreSQL
|
||||
- Optimisation de requêtes
|
||||
- Sauvegarde et restauration
|
||||
- Réplication
|
||||
- Performance tuning
|
||||
- SQL avancé
|
||||
|
||||
### Réseau et Sécurité
|
||||
- **Réseau** :
|
||||
- Configuration de pare-feu
|
||||
- Gestion des ports et services
|
||||
- Load balancing
|
||||
- CDN configuration
|
||||
- DNS, DHCP, VPN, SD-WAN
|
||||
- Configuration réseau avancée
|
||||
- **Sécurité** :
|
||||
- SSL/TLS configuration
|
||||
- Gestion des certificats
|
||||
- Sécurisation des APIs
|
||||
- Protection contre les attaques
|
||||
- Audit de sécurité
|
||||
- Chiffrement des communications et données
|
||||
- Surveillance et détection d'intrusions
|
||||
- Prévention des cyberattaques
|
||||
|
||||
### Monitoring et Observabilité
|
||||
- **Monitoring** :
|
||||
- Monitoring des applications
|
||||
- Monitoring des nœuds blockchain
|
||||
- Alerting
|
||||
- Métriques et dashboards
|
||||
- **Logs** :
|
||||
- Centralisation des logs
|
||||
- Analyse de logs
|
||||
- Rotation des logs
|
||||
|
||||
### CI/CD et Automatisation
|
||||
- **Intégration Continue** :
|
||||
- Configuration de pipelines CI/CD
|
||||
- Tests automatisés
|
||||
- Build automatisé
|
||||
- Déploiement automatisé
|
||||
- **Outils** :
|
||||
- GitHub Actions, GitLab CI, Drone CI
|
||||
- Scripts d'automatisation
|
||||
- Configuration de workflows
|
||||
|
||||
### Automatisation et Scripting
|
||||
- **Scripts** :
|
||||
- Bash scripting avancé
|
||||
- Python scripting pour automatisation
|
||||
- Automatisation de tâches d'administration
|
||||
- Scripts de déploiement
|
||||
- Automatisation des environnements pour cohérence
|
||||
- **Compétences requises** : Scripting, automatisation, amélioration de la cohérence des environnements
|
||||
|
||||
### Infrastructure Cloud/On-Premise
|
||||
- **Cloud** :
|
||||
- Déploiement sur cloud (si applicable)
|
||||
- Gestion de ressources cloud
|
||||
- Auto-scaling
|
||||
- Cloud décentralisé (ThreeFold Grid)
|
||||
- **On-Premise** :
|
||||
- Gestion de serveurs physiques
|
||||
- Virtualisation (VMware, Hyper-V, KVM)
|
||||
- Gestion de l'infrastructure
|
||||
- Provisioning de serveurs
|
||||
- Infrastructure décentralisée (3Nodes)
|
||||
|
||||
### Gestion de Configuration
|
||||
- **Configuration Management** :
|
||||
- Ansible, Puppet, Chef
|
||||
- Infrastructure as Code
|
||||
- Configuration de serveurs
|
||||
- **Versioning** :
|
||||
- Versioning de la configuration
|
||||
- Gestion des environnements (dev, staging, prod)
|
||||
|
||||
### Sauvegarde et Récupération
|
||||
- **Sauvegarde** :
|
||||
- Stratégies de sauvegarde
|
||||
- Sauvegarde des bases de données
|
||||
- Sauvegarde de la configuration
|
||||
- Sauvegarde de la blockchain
|
||||
- **Récupération** :
|
||||
- Plans de reprise après sinistre
|
||||
- Tests de restauration
|
||||
- RTO/RPO
|
||||
|
||||
## Compétences DevOps
|
||||
|
||||
### Container Orchestration
|
||||
- **Kubernetes** : Orchestration de conteneurs (mentionné comme compétence requise)
|
||||
- **Docker Swarm** : Alternative à Kubernetes
|
||||
- **Compétences requises** : Orchestration, scaling, service mesh, gestion de clusters
|
||||
|
||||
### Infrastructure as Code
|
||||
- **Terraform** : Provisioning d'infrastructure
|
||||
- **CloudFormation** : Si AWS
|
||||
- **Compétences requises** : IaC, provisioning automatisé
|
||||
|
||||
### Secrets Management
|
||||
- **Gestion des secrets** : Vault, AWS Secrets Manager
|
||||
- **Compétences requises** : Sécurité des secrets, rotation
|
||||
|
||||
## Compétences Spécialisées Blockchain
|
||||
|
||||
### Cryptographie
|
||||
- **Cryptographie appliquée** :
|
||||
- Signatures cryptographiques
|
||||
- Hashing
|
||||
- Clés publiques/privées
|
||||
- Certificats
|
||||
- **Compétences requises** : Cryptographie, sécurité
|
||||
|
||||
### Consensus et Réseau
|
||||
- **Protocoles de consensus** : Compréhension des mécanismes de consensus
|
||||
- **Réseau P2P** : Gestion de réseaux pair-à-pair
|
||||
- **Compétences requises** : Blockchain, réseaux distribués
|
||||
|
||||
## Résumé des Compétences par Catégorie
|
||||
|
||||
### Développement
|
||||
- Rust (avancé)
|
||||
- Python
|
||||
- JavaScript/TypeScript
|
||||
- Vue.js / Nuxt.js
|
||||
- Substrate Framework
|
||||
- Développement CLI
|
||||
- Extensions navigateur
|
||||
- APIs REST/GraphQL
|
||||
|
||||
### Blockchain
|
||||
- Développement blockchain
|
||||
- Substrate
|
||||
- Consensus algorithms
|
||||
- Cryptographie
|
||||
- Réseaux P2P
|
||||
|
||||
### Web
|
||||
- Frameworks web modernes
|
||||
- JAMstack
|
||||
- CMS headless
|
||||
- Intégrations d'APIs
|
||||
|
||||
### Infrastructure
|
||||
- Administration Linux
|
||||
- Docker/Conteneurisation
|
||||
- CI/CD
|
||||
- Monitoring
|
||||
- Sécurité
|
||||
- Bases de données
|
||||
- Réseau
|
||||
- Infrastructure décentralisée (ThreeFold Grid)
|
||||
- Edge computing
|
||||
- Cloud décentralisé
|
||||
- Zero OS
|
||||
- Stockage distribué
|
||||
|
||||
### DevOps
|
||||
- Automatisation
|
||||
- Infrastructure as Code
|
||||
- Gestion de configuration
|
||||
- Orchestration
|
||||
|
||||
## Compétences Transversales
|
||||
|
||||
### Communication et Collaboration
|
||||
- Travail en équipe avec développeurs et parties prenantes
|
||||
- Communication efficace
|
||||
- Documentation technique
|
||||
- Partage de connaissances
|
||||
|
||||
### Veille Technologique
|
||||
- Suivi des évolutions technologiques
|
||||
- Meilleures pratiques du secteur
|
||||
- Évaluation de nouvelles technologies
|
||||
- Adaptation aux changements
|
||||
|
||||
## Notes
|
||||
|
||||
Cette liste est basée sur l'analyse des projets visibles sur https://git.duniter.org/ et les informations disponibles sur l'écosystème Duniter/Ğ1. Certaines technologies peuvent être utilisées mais non explicitement mentionnées dans les descriptions de projets.
|
||||
|
||||
### Sources
|
||||
- https://git.duniter.org/ - Dépôt principal des projets Duniter
|
||||
- https://www.threefold.io/ - Infrastructure Internet décentralisée ThreeFold
|
||||
- Documentation technique des projets individuels
|
||||
- Analyse des technologies blockchain et monnaies libres
|
||||
- Analyse des infrastructures décentralisées
|
||||
|
||||
### Pour une analyse complète, il serait recommandé de :
|
||||
1. Examiner le code source des projets principaux
|
||||
2. Analyser les fichiers de configuration (package.json, Cargo.toml, requirements.txt, Dockerfile)
|
||||
3. Examiner les fichiers de déploiement (docker-compose.yml, scripts CI/CD)
|
||||
4. Consulter la documentation technique de chaque projet
|
||||
5. Analyser les dépendances et bibliothèques utilisées
|
||||
|
||||
1133
package-lock.json
generated
1133
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "aoe-techradar",
|
||||
"name": "techradar-laplank",
|
||||
"private": true,
|
||||
"version": "4.3.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "techradar build",
|
||||
"serve": "techradar serve",
|
||||
"build": "node scripts/build-radar.js",
|
||||
"serve": "node scripts/serve-radar.js build",
|
||||
"serve-dev": "node scripts/serve-radar.js dev",
|
||||
"serve-business": "./scripts/serve-business.sh",
|
||||
"extract-tech": "node scripts/extract-technologies.js",
|
||||
"analyze-business": "node scripts/analyze-business-metrics.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"aoe_technology_radar": "github:AOEpeople/aoe_technology_radar#main",
|
||||
"glob": "^10.3.10",
|
||||
"gray-matter": "^4.0.3"
|
||||
},
|
||||
|
||||
640
public/_team-content
Normal file
640
public/_team-content
Normal file
@@ -0,0 +1,640 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Équipe & Technologies - Laplank</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/cytoscape@3.26.0/dist/cytoscape.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cose-bilkent@4.1.0/cytoscape-cose-bilkent.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||
<script>
|
||||
console.log('🔧 TEAM.HTML: Scripts externes chargés');
|
||||
console.log('🔧 Cytoscape disponible:', typeof cytoscape !== 'undefined');
|
||||
console.log('🔧 ECharts disponible:', typeof echarts !== 'undefined');
|
||||
</script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: #1a4d3a;
|
||||
color: #e0e0e0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: rgba(26, 77, 58, 0.5);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #4ade80;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
padding: 12px 24px;
|
||||
background: rgba(74, 222, 128, 0.2);
|
||||
border: 2px solid #4ade80;
|
||||
color: #4ade80;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.tab-button:hover {
|
||||
background: rgba(74, 222, 128, 0.3);
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
background: #4ade80;
|
||||
color: #1a4d3a;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
background: rgba(26, 77, 58, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#network-graph {
|
||||
width: 100%;
|
||||
height: 700px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(74, 222, 128, 0.3);
|
||||
}
|
||||
|
||||
#congestion-matrix {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#genesis-team {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.genesis-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: rgba(74, 222, 128, 0.1);
|
||||
border: 1px solid rgba(74, 222, 128, 0.3);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #a0a0a0;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.member-card {
|
||||
background: rgba(26, 77, 58, 0.5);
|
||||
border: 1px solid rgba(74, 222, 128, 0.3);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.member-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
.member-availability {
|
||||
background: rgba(74, 222, 128, 0.2);
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tech-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tech-tag {
|
||||
background: rgba(74, 222, 128, 0.2);
|
||||
border: 1px solid rgba(74, 222, 128, 0.4);
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
background: rgba(255, 68, 68, 0.2);
|
||||
border: 1px solid rgba(255, 68, 68, 0.5);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.warning-title {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.uncovered-tech {
|
||||
background: rgba(255, 68, 68, 0.1);
|
||||
border-left: 3px solid #ff6b6b;
|
||||
padding: 10px;
|
||||
margin: 8px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #4ade80;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<a href="/" style="color: #4ade80; text-decoration: none; font-size: 18px; font-weight: bold;">← Retour au Radar</a>
|
||||
<div></div>
|
||||
</div>
|
||||
<h1>👥 Équipe & Technologies</h1>
|
||||
<p>Visualisation des compétences et identification de l'équipe de genèse MVP</p>
|
||||
</header>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="tab-button active" onclick="showTab('network')">Graphe Réseau</button>
|
||||
<button class="tab-button" onclick="showTab('congestion')">Matrice Congestion</button>
|
||||
<button class="tab-button" onclick="showTab('genesis')">Équipe Genèse MVP</button>
|
||||
</div>
|
||||
|
||||
<div id="network-tab" class="tab-content active">
|
||||
<div class="legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #ff4444;"></div>
|
||||
<span>Core (Critique)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #ff8800;"></div>
|
||||
<span>Strategic</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #4488ff;"></div>
|
||||
<span>Support</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color" style="background: #88ff88;"></div>
|
||||
<span>Membres</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="network-graph"></div>
|
||||
</div>
|
||||
|
||||
<div id="congestion-tab" class="tab-content">
|
||||
<h2 style="margin-bottom: 20px;">Matrice de Congestion - Technologies Core</h2>
|
||||
<div id="congestion-matrix"></div>
|
||||
</div>
|
||||
|
||||
<div id="genesis-tab" class="tab-content">
|
||||
<div id="genesis-team">
|
||||
<div class="loading">Chargement des données...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
console.log('🚀 TEAM.HTML: Script chargé, initialisation...');
|
||||
|
||||
let data = null;
|
||||
let networkCy = null;
|
||||
let congestionChart = null;
|
||||
|
||||
console.log('📋 TEAM.HTML: Fonction loadData() appelée');
|
||||
|
||||
// Charger les données
|
||||
async function loadData() {
|
||||
try {
|
||||
console.log('🔄 Chargement des données équipe depuis /team-visualization-data.json');
|
||||
const response = await fetch('/team-visualization-data.json');
|
||||
console.log('📡 Réponse reçue:', response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
data = await response.json();
|
||||
console.log('✅ Données chargées:', Object.keys(data));
|
||||
console.log('📊 Nombre de nœuds réseau:', data.network?.nodes?.length || 0);
|
||||
console.log('📊 Données matrice congestion:', data.congestionMatrix?.length || 0);
|
||||
console.log('📊 Données équipe genèse:', data.genesisTeam ? 'présentes' : 'absentes');
|
||||
|
||||
initVisualizations();
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors du chargement des données:', error);
|
||||
|
||||
// Fallback : afficher un message d'information si les données ne se chargent pas
|
||||
const fallbackMessage = `
|
||||
<div style="padding: 20px; background: rgba(255, 152, 0, 0.1); border: 1px solid #ff9800; border-radius: 8px; margin: 20px 0;">
|
||||
<h3 style="color: #ff9800; margin-top: 0;">🔄 Chargement des données...</h3>
|
||||
<p>Les visualisations équipe se chargent. Si elles n'apparaissent pas :</p>
|
||||
<ul>
|
||||
<li>Vérifiez la console du navigateur (F12) pour les erreurs</li>
|
||||
<li>Assurez-vous que <code>team-visualization-data.json</code> est accessible</li>
|
||||
<li>Le script <code>generate-team-visualization-data.js</code> doit avoir été exécuté</li>
|
||||
</ul>
|
||||
<p><strong>Erreur détectée :</strong> ${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Afficher le message de fallback dans toutes les sections
|
||||
document.getElementById('network-graph').innerHTML = fallbackMessage;
|
||||
document.getElementById('congestion-matrix').innerHTML = fallbackMessage;
|
||||
document.getElementById('genesis-team').innerHTML = fallbackMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialiser les visualisations
|
||||
function initVisualizations() {
|
||||
console.log('🎨 TEAM.HTML: initVisualizations() appelée');
|
||||
initNetworkGraph();
|
||||
initCongestionMatrix();
|
||||
initGenesisTeam();
|
||||
}
|
||||
|
||||
// Graphe réseau
|
||||
function initNetworkGraph() {
|
||||
console.log('📊 TEAM.HTML: initNetworkGraph() appelée');
|
||||
if (!data || !data.network) {
|
||||
console.log('⚠️ TEAM.HTML: Pas de données réseau');
|
||||
return;
|
||||
}
|
||||
|
||||
networkCy = cytoscape({
|
||||
container: document.getElementById('network-graph'),
|
||||
elements: data.network,
|
||||
style: [
|
||||
{
|
||||
selector: 'node[type="technology"]',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'label': 'data(label)',
|
||||
'width': function(ele) {
|
||||
const coverage = ele.data('coverage') || 0;
|
||||
return Math.max(30, 30 + (coverage * 8));
|
||||
},
|
||||
'height': function(ele) {
|
||||
const coverage = ele.data('coverage') || 0;
|
||||
return Math.max(30, 30 + (coverage * 8));
|
||||
},
|
||||
'color': '#fff',
|
||||
'font-size': '12px',
|
||||
'text-outline-width': 2,
|
||||
'text-outline-color': '#000',
|
||||
'text-wrap': 'wrap',
|
||||
'text-max-width': 100
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'node[type="member"]',
|
||||
style: {
|
||||
'background-color': '#88ff88',
|
||||
'label': 'data(label)',
|
||||
'width': function(ele) {
|
||||
const availability = ele.data('availability') || 0;
|
||||
return Math.max(25, 25 + (availability / 3));
|
||||
},
|
||||
'height': function(ele) {
|
||||
const availability = ele.data('availability') || 0;
|
||||
return Math.max(25, 25 + (availability / 3));
|
||||
},
|
||||
'color': '#1a4d3a',
|
||||
'font-size': '11px',
|
||||
'font-weight': 'bold',
|
||||
'shape': 'ellipse'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': function(ele) {
|
||||
return 1 + (ele.data('weight') || 0.5);
|
||||
},
|
||||
'line-color': '#999',
|
||||
'opacity': 0.6,
|
||||
'curve-style': 'bezier'
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'cose-bilkent',
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
idealEdgeLength: 100,
|
||||
nodeRepulsion: 4500,
|
||||
nestingFactor: 0.1,
|
||||
gravity: 0.25,
|
||||
numIter: 2500,
|
||||
tile: true,
|
||||
animate: true,
|
||||
animationDuration: 1000
|
||||
}
|
||||
});
|
||||
|
||||
// Tooltip sur survol
|
||||
networkCy.on('mouseover', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
const data = node.data();
|
||||
let tooltip = '';
|
||||
|
||||
if (data.type === 'technology') {
|
||||
tooltip = `${data.label}\n` +
|
||||
`Ring: ${data.ring}\n` +
|
||||
`Couverture: ${data.coverage} personne(s)\n` +
|
||||
`Impact: ${data.businessImpact}\n` +
|
||||
`Gap: ${data.skillGap}`;
|
||||
} else {
|
||||
tooltip = `${data.label}\n` +
|
||||
`Disponibilité: ${data.availability}%\n` +
|
||||
`Niveau: ${data.seniority}\n` +
|
||||
(data.role ? `Rôle: ${data.role}` : '');
|
||||
}
|
||||
|
||||
node.tooltip = tooltip;
|
||||
});
|
||||
}
|
||||
|
||||
// Matrice de congestion
|
||||
function initCongestionMatrix() {
|
||||
console.log('📈 TEAM.HTML: initCongestionMatrix() appelée');
|
||||
if (!data || !data.congestionMatrix) {
|
||||
console.log('⚠️ TEAM.HTML: Pas de données matrice congestion');
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = echarts.init(document.getElementById('congestion-matrix'));
|
||||
congestionChart = chart;
|
||||
|
||||
const techs = data.congestionMatrix.map(r => r.technology);
|
||||
const members = data.congestionMatrix[0]?.members.map(m => m.fullName || m.member) || [];
|
||||
|
||||
const heatmapData = [];
|
||||
const scatterData = [];
|
||||
|
||||
data.congestionMatrix.forEach((row, i) => {
|
||||
row.members.forEach((member, j) => {
|
||||
if (member.hasSkill) {
|
||||
heatmapData.push([j, i, member.availability]);
|
||||
scatterData.push({
|
||||
value: [j, i],
|
||||
member: member.fullName || member.member,
|
||||
tech: row.technology,
|
||||
availability: member.availability
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: 'Disponibilité des membres sur les technologies Core',
|
||||
left: 'center',
|
||||
textStyle: { color: '#e0e0e0' }
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function(params) {
|
||||
if (params.data && params.data.member) {
|
||||
return `${params.data.member}<br/>` +
|
||||
`Technologie: ${params.data.tech}<br/>` +
|
||||
`Disponibilité: ${params.data.availability}%`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '60%',
|
||||
top: '15%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: members,
|
||||
axisLabel: {
|
||||
rotate: 45,
|
||||
color: '#e0e0e0',
|
||||
fontSize: 11
|
||||
},
|
||||
axisLine: { lineStyle: { color: '#4ade80' } }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: techs,
|
||||
axisLabel: { color: '#e0e0e0' },
|
||||
axisLine: { lineStyle: { color: '#4ade80' } }
|
||||
},
|
||||
visualMap: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
calculable: true,
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: '5%',
|
||||
inRange: {
|
||||
color: ['#1a4d3a', '#4ade80', '#86efac']
|
||||
},
|
||||
textStyle: { color: '#e0e0e0' }
|
||||
},
|
||||
series: [{
|
||||
name: 'Disponibilité',
|
||||
type: 'scatter',
|
||||
data: scatterData,
|
||||
symbolSize: function(data) {
|
||||
return 15 + (data[2] || 0) / 2;
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#4ade80',
|
||||
borderColor: '#1a4d3a',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function(params) {
|
||||
return params.data.availability + '%';
|
||||
},
|
||||
color: '#1a4d3a',
|
||||
fontSize: 10
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
|
||||
// Redimensionner au resize
|
||||
window.addEventListener('resize', () => chart.resize());
|
||||
}
|
||||
|
||||
// Équipe de genèse
|
||||
function initGenesisTeam() {
|
||||
console.log('👥 TEAM.HTML: initGenesisTeam() appelée');
|
||||
if (!data || !data.genesisTeam) {
|
||||
console.log('⚠️ TEAM.HTML: Pas de données équipe genèse');
|
||||
return;
|
||||
}
|
||||
|
||||
const genesis = data.genesisTeam;
|
||||
const html = `
|
||||
<div class="genesis-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${genesis.totalMembers}</div>
|
||||
<div class="stat-label">Membres sélectionnés</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${genesis.totalCapacity}%</div>
|
||||
<div class="stat-label">Capacité totale</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${genesis.averageAvailability}%</div>
|
||||
<div class="stat-label">Disponibilité moyenne</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${genesis.coveredTechnologies}/${genesis.totalCoreTechnologies}</div>
|
||||
<div class="stat-label">Technologies couvertes</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 20px; color: #4ade80;">Membres de l'équipe de genèse</h2>
|
||||
|
||||
${genesis.team.length > 0 ? genesis.team.map(member => `
|
||||
<div class="member-card">
|
||||
<div class="member-header">
|
||||
<div>
|
||||
<div class="member-name">${member.fullName || member.member}</div>
|
||||
<div style="font-size: 12px; color: #a0a0a0; margin-top: 4px;">
|
||||
${member.role || ''} • ${member.seniority} • ${member.coverage} technologie(s)
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-availability">${member.availability}% dispo</div>
|
||||
</div>
|
||||
<div class="tech-list">
|
||||
${member.technologies.map(tech => `
|
||||
<span class="tech-tag">${tech.title}</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`).join('') : '<p style="color: #a0a0a0;">Aucun membre ne répond aux critères (disponibilité >= 50%).</p>'}
|
||||
|
||||
${genesis.uncoveredTechnologies.length > 0 ? `
|
||||
<div class="warning-box">
|
||||
<div class="warning-title">⚠️ Technologies Core non couvertes</div>
|
||||
<p style="margin-bottom: 10px;">Ces technologies critiques ne sont pas maîtrisées par l'équipe de genèse :</p>
|
||||
${genesis.uncoveredTechnologies.map(tech => `
|
||||
<div class="uncovered-tech">
|
||||
<strong>${tech.title}</strong>
|
||||
<div style="font-size: 12px; color: #a0a0a0; margin-top: 4px;">
|
||||
Impact: ${tech.businessImpact} • Gap: ${tech.skillGap} • Couverture actuelle: ${tech.teamCoverage} personne(s)
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
document.getElementById('genesis-team').innerHTML = html;
|
||||
}
|
||||
|
||||
// Navigation par onglets
|
||||
function showTab(tabName) {
|
||||
// Désactiver tous les onglets
|
||||
document.querySelectorAll('.tab-content').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.tab-button').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
|
||||
// Activer l'onglet sélectionné
|
||||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||
event.target.classList.add('active');
|
||||
|
||||
// Redimensionner les graphiques si nécessaire
|
||||
if (tabName === 'congestion' && congestionChart) {
|
||||
setTimeout(() => congestionChart.resize(), 100);
|
||||
}
|
||||
if (tabName === 'network' && networkCy) {
|
||||
setTimeout(() => networkCy.resize(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Charger au démarrage
|
||||
console.log('🚀 TEAM.HTML: Démarrage - appel loadData()');
|
||||
loadData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
function initStrategyLinks() {
|
||||
addStrategyLinkToFooter();
|
||||
addStrategyLinkToHeader();
|
||||
// DÉSACTIVÉ: addStrategyLinkToHeader() - Les liens sont maintenant gérés par Navigation.tsx
|
||||
// addStrategyLinkToHeader();
|
||||
handleStrategyRoute();
|
||||
}
|
||||
|
||||
@@ -94,8 +95,8 @@
|
||||
<h1>Stratégie d'Évolution Technique - Laplank</h1>
|
||||
<p><strong>Date de mise à jour</strong> : 02/12/2025</p>
|
||||
<p>La stratégie complète est disponible dans le dépôt Git :</p>
|
||||
<p><a href="https://git.open.us.org/AJR/TechradarDev/-/blob/dev-biz/docs/strategie-evolution-technique.md" target="_blank">Voir la stratégie sur GitLab</a></p>
|
||||
<p>Ou consultez le fichier local : <code>docs/strategie-evolution-technique.md</code></p>
|
||||
<p><a href="https://git.open.us.org/AJR/TechradarDev/-/blob/dev-biz/docs/data/strategie-evolution-technique.md" target="_blank">Voir la stratégie sur GitLab</a></p>
|
||||
<p>Ou consultez le fichier local : <code>docs/data/strategie-evolution-technique.md</code></p>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -123,20 +124,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
function addStrategyLinkToHeader() {
|
||||
// Chercher le header ou la navigation
|
||||
const header = document.querySelector('header') || document.querySelector('nav') || document.querySelector('[class*="header"]') || document.querySelector('[class*="nav"]');
|
||||
|
||||
if (header) {
|
||||
const strategyLink = document.createElement('a');
|
||||
strategyLink.href = '/strategie';
|
||||
strategyLink.textContent = 'Stratégie';
|
||||
strategyLink.style.marginLeft = '15px';
|
||||
strategyLink.style.color = '#2ecc71';
|
||||
strategyLink.style.textDecoration = 'none';
|
||||
strategyLink.style.fontWeight = 'bold';
|
||||
|
||||
header.appendChild(strategyLink);
|
||||
}
|
||||
}
|
||||
// FONCTION DÉSACTIVÉE: Les liens de navigation sont maintenant gérés par Navigation.tsx
|
||||
// Cette fonction créait des doublons dans le header
|
||||
// function addStrategyLinkToHeader() {
|
||||
// ... code désactivé ...
|
||||
// }
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,99 @@
|
||||
// Script pour la gestion des pages de stratégie
|
||||
// SCRIPT ULTRA-PROTECTEUR CONTRE LES PAGES ÉQUIPE
|
||||
(function() {
|
||||
// VÉRIFICATION ABSOLUE - S'EXÉCUTE AVANT TOUT
|
||||
if (window.location.pathname === '/team' ||
|
||||
window.location.pathname.startsWith('/team/') ||
|
||||
window.location.href.includes('/team')) {
|
||||
console.log('🚫 INTERDICTION TOTALE - Page équipe détectée, arrêt du script');
|
||||
// Bloquer complètement l'exécution
|
||||
window.__blockAllScripts = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Marquer qu'on autorise les autres scripts
|
||||
window.__allowScripts = true;
|
||||
})();
|
||||
|
||||
// Script pour la gestion des pages de stratégie UNIQUEMENT SI AUTORISÉ
|
||||
if (!window.__blockAllScripts && !window.__blockTeamPages) {
|
||||
|
||||
// PROTECTION : Ne pas interférer avec les pages Next.js
|
||||
function shouldSkipExecution() {
|
||||
// Vérifier les indicateurs Next.js présents dès le chargement
|
||||
if (document.querySelector('[data-reactroot]') ||
|
||||
window.__NEXT_DATA__ ||
|
||||
window.next ||
|
||||
document.querySelector('#__next') ||
|
||||
document.querySelector('[data-react-helmet]')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier immédiatement
|
||||
if (shouldSkipExecution()) {
|
||||
console.log('🚫 Script stratégie désactivé sur page Next.js');
|
||||
return;
|
||||
}
|
||||
|
||||
// Différer la vérification au cas où Next.js se charge après
|
||||
setTimeout(function() {
|
||||
if (shouldSkipExecution()) {
|
||||
console.log('🚫 Script stratégie désactivé après délai (page Next.js détectée)');
|
||||
// Arrêter toute exécution en cours
|
||||
if (window.initStrategyTimeout) clearTimeout(window.initStrategyTimeout);
|
||||
return;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Protection contre Fast Refresh : ignorer les tentatives de hot-reload
|
||||
// Intercepter et ignorer les requêtes webpack hot-update pour éviter les rechargements en boucle
|
||||
if (typeof window !== 'undefined') {
|
||||
// Intercepter fetch
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function(...args) {
|
||||
const url = args[0];
|
||||
if (typeof url === 'string' && url.includes('webpack.hot-update.json')) {
|
||||
// Ignorer silencieusement les requêtes webpack hot-update
|
||||
return Promise.resolve(new Response(JSON.stringify({}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}));
|
||||
}
|
||||
return originalFetch.apply(this, args);
|
||||
};
|
||||
|
||||
// Intercepter XMLHttpRequest (utilisé par Next.js pour Fast Refresh)
|
||||
const originalXHROpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
||||
if (typeof url === 'string' && url.includes('webpack.hot-update.json')) {
|
||||
// Créer un faux XHR qui ne fait rien
|
||||
this._shouldIgnore = true;
|
||||
return;
|
||||
}
|
||||
return originalXHROpen.apply(this, [method, url, ...rest]);
|
||||
};
|
||||
|
||||
const originalXHRSend = XMLHttpRequest.prototype.send;
|
||||
XMLHttpRequest.prototype.send = function(...args) {
|
||||
if (this._shouldIgnore) {
|
||||
// Simuler une réponse réussie pour éviter les erreurs
|
||||
setTimeout(() => {
|
||||
if (this.onload) this.onload();
|
||||
if (this.onreadystatechange) {
|
||||
this.readyState = 4;
|
||||
this.status = 200;
|
||||
this.responseText = '{}';
|
||||
this.onreadystatechange();
|
||||
}
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
return originalXHRSend.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
// --- DÉBUT PROTECTION MOT DE PASSE ---
|
||||
function checkAuth() {
|
||||
const SESSION_KEY = 'radar_auth_session';
|
||||
@@ -706,9 +800,46 @@ Interface de pilotage pour les responsables sécurité des PME.
|
||||
return html;
|
||||
}
|
||||
|
||||
// Protection contre les exécutions multiples
|
||||
let isInitialized = false;
|
||||
let initTimeout = null;
|
||||
|
||||
function initStrategyLinks() {
|
||||
addLinksToHeader();
|
||||
// Éviter les exécutions multiples
|
||||
if (isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Annuler toute tentative d'initialisation en cours
|
||||
if (initTimeout) {
|
||||
clearTimeout(initTimeout);
|
||||
initTimeout = null;
|
||||
}
|
||||
|
||||
console.log('🚀 initStrategyLinks() appelée');
|
||||
|
||||
// Marquer comme initialisé
|
||||
isInitialized = true;
|
||||
|
||||
// DÉSACTIVÉ: addLinksToHeader() - Les liens sont maintenant gérés par Navigation.tsx
|
||||
// addLinksToHeader();
|
||||
|
||||
// Vérifier la route immédiatement (surtout pour /team)
|
||||
handleRoute();
|
||||
|
||||
// Intercepter les clics sur les liens /team pour éviter la navigation Next.js
|
||||
document.addEventListener('click', function(e) {
|
||||
const link = e.target.closest('a');
|
||||
if (link) {
|
||||
const href = link.getAttribute('href') || link.href;
|
||||
if (href && (href.includes('/team') || href === '/team' || href === '/team/' || href === '/team.html')) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
showTeamPage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}, true); // Utiliser capture phase pour intercepter avant Next.js
|
||||
}
|
||||
|
||||
function normalizePath(pathname) {
|
||||
@@ -720,14 +851,34 @@ Interface de pilotage pour les responsables sécurité des PME.
|
||||
function handleRoute() {
|
||||
const path = normalizePath(window.location.pathname);
|
||||
const hash = window.location.hash;
|
||||
|
||||
// Détection simple
|
||||
|
||||
// IMPORTANT : Ne jamais gérer /team dans ce script - Next.js s'en charge exclusivement
|
||||
// Détection simple pour les pages HTML pures uniquement
|
||||
if (hash === '#strategie' || path === '/strategie') showPage('strategie');
|
||||
else if (hash === '#business' || path === '/business') showPage('business');
|
||||
else if (hash === '#dataviz' || path === '/dataviz') showPage('dataviz');
|
||||
else if (hash === '#dataviz-details' || path === '/dataviz-details') showPage('dataviz-details');
|
||||
}
|
||||
|
||||
// Vérifier la route /team IMMÉDIATEMENT au chargement du script (avant Next.js)
|
||||
(function checkTeamRouteImmediately() {
|
||||
const path = normalizePath(window.location.pathname);
|
||||
if (path === '/team' || path === '/team/') {
|
||||
console.log('🔍 Route /team détectée immédiatement, affichage...');
|
||||
// Attendre que le body soit disponible
|
||||
if (document.body) {
|
||||
showTeamPage();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', showTeamPage);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// FONCTION DÉSACTIVÉE: showTeamPage() - Next.js gère maintenant la page /team
|
||||
// function showTeamPage() {
|
||||
// // Cette fonction n'est plus nécessaire car Next.js charge directement l'iframe
|
||||
// }
|
||||
|
||||
function showPage(pageId) {
|
||||
if (!pagesContent[pageId]) return;
|
||||
|
||||
@@ -764,7 +915,10 @@ Interface de pilotage pour les responsables sécurité des PME.
|
||||
if (window.originalBodyContent) {
|
||||
document.body.innerHTML = window.originalBodyContent;
|
||||
window.history.pushState(null, null, '/');
|
||||
setTimeout(initStrategyLinks, 100);
|
||||
// Réinitialiser le flag pour permettre la réinitialisation
|
||||
isInitialized = false;
|
||||
if (initTimeout) clearTimeout(initTimeout);
|
||||
initTimeout = setTimeout(initStrategyLinks, 100);
|
||||
} else {
|
||||
window.location.href = '/';
|
||||
}
|
||||
@@ -816,79 +970,108 @@ Interface de pilotage pour les responsables sécurité des PME.
|
||||
});
|
||||
}
|
||||
|
||||
function addLinksToHeader() {
|
||||
// Chercher le header
|
||||
const header = document.querySelector('header') ||
|
||||
document.querySelector('nav') ||
|
||||
document.querySelector('div[role="banner"]');
|
||||
|
||||
let container = header;
|
||||
|
||||
// Si pas de header, créer une barre fixe
|
||||
if (!header) {
|
||||
let fixedBar = document.getElementById('custom-nav-bar');
|
||||
if (!fixedBar) {
|
||||
fixedBar = document.createElement('div');
|
||||
fixedBar.id = 'custom-nav-bar';
|
||||
fixedBar.style.cssText = 'position: fixed; top: 0; right: 0; padding: 10px 20px; z-index: 9999; display: flex; gap: 15px; background: rgba(255,255,255,0.9); border-bottom-left-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);';
|
||||
document.body.appendChild(fixedBar);
|
||||
}
|
||||
container = fixedBar;
|
||||
} else {
|
||||
// Si header existant, on s'assure qu'on n'a pas déjà ajouté les liens
|
||||
if (header.querySelector('.custom-nav-link')) return;
|
||||
|
||||
// Créer un conteneur pour nos liens s'il n'existe pas
|
||||
let linkContainer = document.createElement('div');
|
||||
linkContainer.style.cssText = 'display: flex; gap: 15px; margin-left: auto; align-items: center; padding-right: 20px;';
|
||||
header.appendChild(linkContainer);
|
||||
container = linkContainer;
|
||||
}
|
||||
|
||||
// Ajouter les liens
|
||||
Object.keys(pageTitles).forEach(key => {
|
||||
if (document.getElementById(`link-${key}`)) return;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.id = `link-${key}`;
|
||||
link.className = 'custom-nav-link';
|
||||
link.href = `#${key}`;
|
||||
link.textContent = pageTitles[key];
|
||||
link.style.cssText = 'color: #2ecc71; text-decoration: none; font-weight: bold; cursor: pointer; font-size: 14px; padding: 5px 8px; border-radius: 4px; transition: background 0.2s;';
|
||||
|
||||
link.addEventListener('mouseenter', () => link.style.background = 'rgba(46, 204, 113, 0.1)');
|
||||
link.addEventListener('mouseleave', () => link.style.background = 'transparent');
|
||||
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
showPage(key);
|
||||
});
|
||||
|
||||
container.appendChild(link);
|
||||
});
|
||||
}
|
||||
// FONCTION DÉSACTIVÉE: Les liens de navigation sont maintenant gérés par Navigation.tsx
|
||||
// Cette fonction créait des doublons dans le header
|
||||
// function addLinksToHeader() {
|
||||
// ... code désactivé ...
|
||||
// }
|
||||
|
||||
window.addEventListener('popstate', function(event) {
|
||||
if (event.state && event.state.page) {
|
||||
showPage(event.state.page);
|
||||
// DÉSACTIVÉ: Gestion du popstate pour /team - Next.js gère cette route
|
||||
// if (event.state.page === 'team') {
|
||||
// showTeamPage();
|
||||
// } else {
|
||||
showPage(event.state.page);
|
||||
// }
|
||||
} else if (window.originalBodyContent) {
|
||||
document.body.innerHTML = window.originalBodyContent;
|
||||
setTimeout(initStrategyLinks, 100);
|
||||
// Réinitialiser le flag pour permettre la réinitialisation
|
||||
isInitialized = false;
|
||||
if (initTimeout) clearTimeout(initTimeout);
|
||||
initTimeout = setTimeout(initStrategyLinks, 100);
|
||||
}
|
||||
});
|
||||
|
||||
// --- EXÉCUTION AU CHARGEMENT DE LA PAGE ---
|
||||
// Déplacé à la fin pour s'assurer que toutes les fonctions et variables sont définies
|
||||
if (document.readyState === 'loading') {
|
||||
// Bloquer le rendu visuel immédiat si possible
|
||||
document.documentElement.style.display = 'none';
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.documentElement.style.display = '';
|
||||
// Protection globale contre les exécutions multiples
|
||||
if (window.__strategieScriptLoaded) {
|
||||
return; // Script déjà chargé, ne pas réexécuter
|
||||
}
|
||||
window.__strategieScriptLoaded = true;
|
||||
|
||||
// DÉSACTIVÉ: Interception des clics /team - Next.js gère maintenant cette navigation
|
||||
// Plus besoin d'intercepter les clics car Next.js route vers /team correctement
|
||||
|
||||
// Éviter de modifier document.documentElement qui peut déclencher des rechargements Fast Refresh
|
||||
// Utiliser MutationObserver pour détecter quand le header est ajouté
|
||||
function waitForHeaderAndInit() {
|
||||
const header = document.querySelector('header') ||
|
||||
document.querySelector('nav') ||
|
||||
document.querySelector('div[role="banner"]');
|
||||
|
||||
if (header) {
|
||||
console.log('✅ Header trouvé, initialisation...');
|
||||
checkAuth();
|
||||
setTimeout(() => {
|
||||
initStrategyLinks();
|
||||
}, 300);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Essayer immédiatement
|
||||
if (waitForHeaderAndInit()) {
|
||||
// Déjà trouvé, c'est bon
|
||||
} else {
|
||||
// Utiliser MutationObserver pour détecter l'ajout du header
|
||||
const observer = new MutationObserver((mutations, obs) => {
|
||||
if (waitForHeaderAndInit()) {
|
||||
obs.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Observer les changements dans le body
|
||||
if (document.body) {
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
} else {
|
||||
// Attendre que le body soit disponible
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Body déjà là mais pas de header, observer quand même
|
||||
setTimeout(() => {
|
||||
if (document.body) {
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout de sécurité après 5 secondes
|
||||
setTimeout(() => {
|
||||
observer.disconnect();
|
||||
if (!isInitialized) {
|
||||
console.warn('⚠️ Timeout: initialisation forcée après 5 secondes');
|
||||
checkAuth();
|
||||
initStrategyLinks();
|
||||
});
|
||||
} else {
|
||||
checkAuth();
|
||||
initStrategyLinks();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Fermeture du bloc conditionnel - script s'exécute uniquement si pas sur page équipe
|
||||
}
|
||||
|
||||
500
public/team-block-script.js
Normal file
500
public/team-block-script.js
Normal file
@@ -0,0 +1,500 @@
|
||||
// SCRIPT EQUIPE - Remplacement DOM apres chargement
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Fonction pour verifier si on est sur la route team
|
||||
function isTeamRoute() {
|
||||
var path = window.location.pathname;
|
||||
return path === '/team' || path === '/team/' || path.startsWith('/team/');
|
||||
}
|
||||
|
||||
// Fonction pour verifier et initialiser la page team
|
||||
function checkAndInitTeamPage() {
|
||||
if (!isTeamRoute()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Eviter double initialisation
|
||||
if (window.__teamPageInitialized) {
|
||||
return;
|
||||
}
|
||||
window.__teamPageInitialized = true;
|
||||
|
||||
console.log('EQUIPE: Page team detectee, preparation du remplacement DOM');
|
||||
initTeamPage();
|
||||
}
|
||||
|
||||
// Fonction pour charger un script externe
|
||||
function loadScript(src) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// Fonction pour initialiser la page equipe
|
||||
async function initTeamPage() {
|
||||
console.log('EQUIPE: Initialisation de la page');
|
||||
|
||||
// CSS de la page
|
||||
var css = '*{margin:0;padding:0;box-sizing:border-box}' +
|
||||
'body{font-family:system-ui,sans-serif;background:#1a4d3a;color:#e0e0e0;padding:20px}' +
|
||||
'.container{max-width:1400px;margin:0 auto}' +
|
||||
'header{text-align:center;margin-bottom:30px;padding:20px;background:rgba(26,77,58,0.5);border-radius:8px}' +
|
||||
'h1{color:#4ade80;margin-bottom:10px}' +
|
||||
'.tabs{display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap}' +
|
||||
'.tab-btn{padding:12px 24px;background:rgba(74,222,128,0.2);border:2px solid #4ade80;color:#4ade80;border-radius:6px;cursor:pointer;font-size:14px;font-weight:600}' +
|
||||
'.tab-btn:hover{background:rgba(74,222,128,0.3)}' +
|
||||
'.tab-btn.active{background:#4ade80;color:#1a4d3a}' +
|
||||
'.tab-content{display:none;background:rgba(26,77,58,0.3);border-radius:8px;padding:20px;margin-bottom:20px}' +
|
||||
'.tab-content.active{display:block}' +
|
||||
'#network-graph{width:100%;height:700px;background:rgba(0,0,0,0.2);border-radius:8px;border:1px solid rgba(74,222,128,0.3)}' +
|
||||
'#congestion-matrix{width:100%;height:600px;background:rgba(0,0,0,0.2);border-radius:8px}' +
|
||||
'.genesis-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:15px;margin-bottom:30px}' +
|
||||
'.stat-card{background:rgba(74,222,128,0.1);border:1px solid rgba(74,222,128,0.3);border-radius:6px;padding:15px}' +
|
||||
'.stat-value{font-size:32px;font-weight:bold;color:#4ade80}' +
|
||||
'.stat-label{font-size:14px;color:#a0a0a0;margin-top:5px}' +
|
||||
'.member-card{background:rgba(26,77,58,0.5);border:1px solid rgba(74,222,128,0.3);border-radius:6px;padding:15px;margin-bottom:15px}' +
|
||||
'.member-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}' +
|
||||
'.member-name{font-size:18px;font-weight:bold;color:#4ade80}' +
|
||||
'.member-avail{background:rgba(74,222,128,0.2);padding:5px 12px;border-radius:4px;font-size:14px}' +
|
||||
'.tech-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}' +
|
||||
'.tech-tag{background:rgba(74,222,128,0.2);border:1px solid rgba(74,222,128,0.4);padding:4px 10px;border-radius:4px;font-size:12px}' +
|
||||
'.warning-box{background:rgba(255,68,68,0.2);border:1px solid rgba(255,68,68,0.5);border-radius:6px;padding:15px;margin-top:20px}' +
|
||||
'.warning-title{color:#ff6b6b;font-weight:bold;margin-bottom:10px}' +
|
||||
'.uncovered{background:rgba(255,68,68,0.1);border-left:3px solid #ff6b6b;padding:10px;margin:8px 0;border-radius:4px}' +
|
||||
'.legend{display:flex;gap:20px;margin:20px 0;flex-wrap:wrap}' +
|
||||
'.legend-item{display:flex;align-items:center;gap:8px}' +
|
||||
'.legend-color{width:20px;height:20px;border-radius:4px}' +
|
||||
'.loading{text-align:center;padding:40px;color:#4ade80}' +
|
||||
'.clickable{cursor:pointer;transition:all 0.2s}' +
|
||||
'.clickable:hover{transform:scale(1.02);box-shadow:0 4px 12px rgba(74,222,128,0.3)}' +
|
||||
'.profile-modal{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);display:flex;align-items:center;justify-content:center;z-index:9999;animation:fadeIn 0.2s}' +
|
||||
'@keyframes fadeIn{from{opacity:0}to{opacity:1}}' +
|
||||
'.profile-card{background:#1a4d3a;border:2px solid #4ade80;border-radius:12px;padding:25px;max-width:500px;width:90%;max-height:85vh;overflow-y:auto;position:relative;box-shadow:0 20px 60px rgba(0,0,0,0.5)}' +
|
||||
'.profile-card .close-btn{position:absolute;top:15px;right:15px;background:none;border:none;color:#ff6b6b;font-size:28px;cursor:pointer;line-height:1}' +
|
||||
'.profile-card .close-btn:hover{color:#ff4444}' +
|
||||
'.profile-card h2{color:#4ade80;margin-bottom:5px;font-size:24px}' +
|
||||
'.profile-card .role{color:#a0a0a0;font-size:14px;margin-bottom:15px}' +
|
||||
'.profile-card .stats{display:flex;gap:15px;flex-wrap:wrap;margin-bottom:20px}' +
|
||||
'.profile-card .stat{background:rgba(74,222,128,0.15);padding:8px 12px;border-radius:6px;font-size:13px}' +
|
||||
'.profile-card .stat strong{color:#4ade80}' +
|
||||
'.profile-card h3{color:#4ade80;font-size:16px;margin:15px 0 10px;border-bottom:1px solid rgba(74,222,128,0.3);padding-bottom:5px}' +
|
||||
'.profile-card .skills{display:flex;flex-wrap:wrap;gap:8px}' +
|
||||
'.profile-card .skill-tag{padding:5px 10px;border-radius:4px;font-size:12px;background:rgba(74,222,128,0.2);border:1px solid rgba(74,222,128,0.4)}' +
|
||||
'.profile-card .skill-tag.expert{background:rgba(74,222,128,0.4);border-color:#4ade80}' +
|
||||
'.profile-card .skill-tag.intermediate{background:rgba(57,151,212,0.3);border-color:#3997d4}' +
|
||||
'.profile-card .skill-tag.beginner{background:rgba(245,179,54,0.2);border-color:#f5b336}' +
|
||||
'.profile-card .interests{display:flex;flex-wrap:wrap;gap:6px}' +
|
||||
'.profile-card .interest{background:rgba(136,255,136,0.15);padding:4px 8px;border-radius:4px;font-size:11px;color:#88ff88}' +
|
||||
'.profile-card .projects{list-style:none;padding:0}' +
|
||||
'.profile-card .projects li{padding:6px 0;border-bottom:1px solid rgba(255,255,255,0.1);font-size:13px}' +
|
||||
'.profile-card .projects li:last-child{border-bottom:none}' +
|
||||
'.profile-card .bio{color:#c0c0c0;font-size:13px;line-height:1.5;margin-top:15px;font-style:italic}';
|
||||
|
||||
// HTML de la page
|
||||
var html = '<div class="container">' +
|
||||
'<header>' +
|
||||
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">' +
|
||||
'<a href="/" style="color:#4ade80;text-decoration:none;font-size:18px;font-weight:bold">← Retour au Radar</a>' +
|
||||
'<div></div>' +
|
||||
'</div>' +
|
||||
'<h1>Equipe & Technologies</h1>' +
|
||||
'<p>Visualisation des competences et identification de l\'equipe de genese MVP</p>' +
|
||||
'</header>' +
|
||||
'<div class="tabs">' +
|
||||
'<button class="tab-btn active" data-tab="network">Graphe Reseau</button>' +
|
||||
'<button class="tab-btn" data-tab="congestion">Matrice Congestion</button>' +
|
||||
'<button class="tab-btn" data-tab="genesis">Equipe Genese MVP</button>' +
|
||||
'</div>' +
|
||||
'<div id="network-tab" class="tab-content active">' +
|
||||
'<div class="legend">' +
|
||||
'<div class="legend-item"><div class="legend-color" style="background:#4ade80"></div><span>Adopt</span></div>' +
|
||||
'<div class="legend-item"><div class="legend-color" style="background:#3997d4"></div><span>Trial</span></div>' +
|
||||
'<div class="legend-item"><div class="legend-color" style="background:#f5b336"></div><span>Assess</span></div>' +
|
||||
'<div class="legend-item"><div class="legend-color" style="background:#e5695e"></div><span>Hold</span></div>' +
|
||||
'<div class="legend-item"><div class="legend-color" style="background:#88ff88"></div><span>Membres</span></div>' +
|
||||
'</div>' +
|
||||
'<div id="network-graph"><div class="loading">Chargement du graphe...</div></div>' +
|
||||
'</div>' +
|
||||
'<div id="congestion-tab" class="tab-content">' +
|
||||
'<h2 style="margin-bottom:20px">Matrice de Congestion - Technologies Adopt</h2>' +
|
||||
'<div id="congestion-matrix"><div class="loading">Chargement de la matrice...</div></div>' +
|
||||
'</div>' +
|
||||
'<div id="genesis-tab" class="tab-content">' +
|
||||
'<div id="genesis-team"><div class="loading">Chargement des donnees...</div></div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
// Injecter le CSS
|
||||
var styleEl = document.createElement('style');
|
||||
styleEl.textContent = css;
|
||||
document.head.appendChild(styleEl);
|
||||
|
||||
// Modifier le titre
|
||||
document.title = 'Equipe & Technologies - Laplank';
|
||||
|
||||
// Remplacer le contenu du body
|
||||
document.body.innerHTML = html;
|
||||
document.body.style.cssText = 'font-family:system-ui,sans-serif;background:#1a4d3a;color:#e0e0e0;padding:20px;margin:0';
|
||||
|
||||
// Ajouter les gestionnaires d'evenements pour les onglets
|
||||
document.querySelectorAll('.tab-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var tabName = this.getAttribute('data-tab');
|
||||
document.querySelectorAll('.tab-content').forEach(function(t) { t.classList.remove('active'); });
|
||||
document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
|
||||
document.getElementById(tabName + '-tab').classList.add('active');
|
||||
this.classList.add('active');
|
||||
|
||||
// Redimensionner les graphiques
|
||||
if (tabName === 'congestion' && window.congestionChart) {
|
||||
setTimeout(function() { window.congestionChart.resize(); }, 100);
|
||||
}
|
||||
if (tabName === 'network' && window.networkCy) {
|
||||
setTimeout(function() { window.networkCy.resize(); }, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('EQUIPE: DOM remplace, chargement des bibliotheques...');
|
||||
|
||||
// Charger les bibliotheques externes
|
||||
try {
|
||||
await loadScript('https://cdn.jsdelivr.net/npm/cytoscape@3.26.0/dist/cytoscape.min.js');
|
||||
console.log('EQUIPE: Cytoscape charge');
|
||||
// Utilisation du layout cose integre (pas de plugin externe necessaire)
|
||||
|
||||
await loadScript('https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js');
|
||||
console.log('EQUIPE: ECharts charge');
|
||||
|
||||
// Charger les donnees
|
||||
var response = await fetch('/team-visualization-data.json');
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
var data = await response.json();
|
||||
console.log('EQUIPE: Donnees chargees:', Object.keys(data));
|
||||
|
||||
// Stocker les profils pour acces global
|
||||
window.__memberProfiles = data.memberProfiles || {};
|
||||
|
||||
// Initialiser les visualisations
|
||||
initNetwork(data);
|
||||
initCongestion(data);
|
||||
initGenesis(data);
|
||||
|
||||
} catch (e) {
|
||||
console.error('EQUIPE: Erreur:', e);
|
||||
var errorMsg = '<div style="padding:20px;background:rgba(255,152,0,0.2);border:1px solid #ff9800;border-radius:8px"><h3 style="color:#ff9800">Erreur de chargement</h3><p>' + e.message + '</p></div>';
|
||||
document.getElementById('network-graph').innerHTML = errorMsg;
|
||||
document.getElementById('congestion-matrix').innerHTML = errorMsg;
|
||||
document.getElementById('genesis-team').innerHTML = errorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
// Graphe reseau avec Cytoscape
|
||||
function initNetwork(data) {
|
||||
if (!data || !data.network) {
|
||||
document.getElementById('network-graph').innerHTML = '<div class="loading">Pas de donnees reseau</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
window.networkCy = cytoscape({
|
||||
container: document.getElementById('network-graph'),
|
||||
elements: data.network,
|
||||
style: [
|
||||
{
|
||||
selector: 'node[type="technology"]',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'label': 'data(label)',
|
||||
'width': function(e) { return 30 + (e.data('coverage') || 0) * 8; },
|
||||
'height': function(e) { return 30 + (e.data('coverage') || 0) * 8; },
|
||||
'color': '#fff',
|
||||
'font-size': '12px',
|
||||
'text-outline-width': 2,
|
||||
'text-outline-color': '#000',
|
||||
'text-wrap': 'wrap',
|
||||
'text-max-width': 100
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'node[type="member"]',
|
||||
style: {
|
||||
'background-color': '#88ff88',
|
||||
'label': 'data(label)',
|
||||
'width': function(e) { return 35 + (e.data('availability') || 0) / 2; },
|
||||
'height': function(e) { return 35 + (e.data('availability') || 0) / 2; },
|
||||
'color': '#fff',
|
||||
'font-size': '14px',
|
||||
'font-weight': 'bold',
|
||||
'text-outline-width': 2,
|
||||
'text-outline-color': '#000',
|
||||
'shape': 'ellipse'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': function(e) { return 1 + (e.data('weight') || 0.5); },
|
||||
'line-color': '#999',
|
||||
'opacity': 0.6,
|
||||
'curve-style': 'bezier'
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'cose',
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
idealEdgeLength: 100,
|
||||
nodeRepulsion: 4500,
|
||||
gravity: 0.25,
|
||||
numIter: 1000,
|
||||
animate: true,
|
||||
animationDuration: 800
|
||||
}
|
||||
});
|
||||
|
||||
// Ajouter evenement de clic sur les membres
|
||||
window.networkCy.on('tap', 'node[type="member"]', function(evt) {
|
||||
var node = evt.target;
|
||||
var memberId = node.data('id').replace('member-', '');
|
||||
if (window.__memberProfiles && window.__memberProfiles[memberId]) {
|
||||
showMemberProfile(memberId, window.__memberProfiles[memberId]);
|
||||
}
|
||||
});
|
||||
|
||||
// Style curseur pour les membres
|
||||
window.networkCy.on('mouseover', 'node[type="member"]', function() {
|
||||
document.body.style.cursor = 'pointer';
|
||||
});
|
||||
window.networkCy.on('mouseout', 'node[type="member"]', function() {
|
||||
document.body.style.cursor = 'default';
|
||||
});
|
||||
|
||||
console.log('EQUIPE: Graphe reseau initialise');
|
||||
}
|
||||
|
||||
// Matrice de congestion avec ECharts
|
||||
function initCongestion(data) {
|
||||
if (!data || !data.congestionMatrix || data.congestionMatrix.length === 0) {
|
||||
document.getElementById('congestion-matrix').innerHTML = '<div class="loading">Pas de donnees matrice</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var chart = echarts.init(document.getElementById('congestion-matrix'));
|
||||
window.congestionChart = chart;
|
||||
|
||||
var techs = data.congestionMatrix.map(function(r) { return r.technology; });
|
||||
var members = data.congestionMatrix[0] ? data.congestionMatrix[0].members.map(function(m) { return m.fullName || m.member; }) : [];
|
||||
var scatter = [];
|
||||
|
||||
data.congestionMatrix.forEach(function(row, i) {
|
||||
row.members.forEach(function(m, j) {
|
||||
if (m.hasSkill) {
|
||||
scatter.push({
|
||||
value: [j, i],
|
||||
member: m.fullName || m.member,
|
||||
memberId: m.member,
|
||||
tech: row.technology,
|
||||
availability: m.availability
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
chart.setOption({
|
||||
title: { text: 'Disponibilite des membres sur les technologies Adopt', left: 'center', textStyle: { color: '#e0e0e0' } },
|
||||
tooltip: {
|
||||
formatter: function(p) {
|
||||
return p.data.member + '<br/>Tech: ' + p.data.tech + '<br/>Dispo: ' + p.data.availability + '%';
|
||||
}
|
||||
},
|
||||
grid: { height: '60%', top: '15%' },
|
||||
xAxis: { type: 'category', data: members, axisLabel: { rotate: 45, color: '#e0e0e0' }, axisLine: { lineStyle: { color: '#4ade80' } } },
|
||||
yAxis: { type: 'category', data: techs, axisLabel: { color: '#e0e0e0' }, axisLine: { lineStyle: { color: '#4ade80' } } },
|
||||
visualMap: { min: 0, max: 100, calculable: true, orient: 'horizontal', left: 'center', bottom: '5%', inRange: { color: ['#1a4d3a', '#4ade80', '#86efac'] }, textStyle: { color: '#e0e0e0' } },
|
||||
series: [{
|
||||
type: 'scatter',
|
||||
data: scatter,
|
||||
symbolSize: function(d) { return 15 + (d[2] || 0) / 2; },
|
||||
itemStyle: { color: '#4ade80', borderColor: '#1a4d3a', borderWidth: 2 },
|
||||
label: { show: true, formatter: function(p) { return p.data.availability + '%'; }, color: '#1a4d3a' }
|
||||
}]
|
||||
});
|
||||
|
||||
window.addEventListener('resize', function() { chart.resize(); });
|
||||
|
||||
// Ajouter evenement de clic sur les points (membres)
|
||||
chart.on('click', function(params) {
|
||||
if (params.data && params.data.memberId) {
|
||||
var memberId = params.data.memberId;
|
||||
if (window.__memberProfiles && window.__memberProfiles[memberId]) {
|
||||
showMemberProfile(memberId, window.__memberProfiles[memberId]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('EQUIPE: Matrice de congestion initialisee');
|
||||
}
|
||||
|
||||
// Equipe de genese
|
||||
function initGenesis(data) {
|
||||
if (!data || !data.genesisTeam) {
|
||||
document.getElementById('genesis-team').innerHTML = '<div class="loading">Pas de donnees equipe</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var g = data.genesisTeam;
|
||||
var h = '<div class="genesis-stats">' +
|
||||
'<div class="stat-card"><div class="stat-value">' + g.totalMembers + '</div><div class="stat-label">Membres selectionnes</div></div>' +
|
||||
'<div class="stat-card"><div class="stat-value">' + g.totalCapacity + '%</div><div class="stat-label">Capacite totale</div></div>' +
|
||||
'<div class="stat-card"><div class="stat-value">' + g.averageAvailability + '%</div><div class="stat-label">Disponibilite moyenne</div></div>' +
|
||||
'<div class="stat-card"><div class="stat-value">' + g.coveredTechnologies + '/' + g.totalCoreTechnologies + '</div><div class="stat-label">Technologies couvertes</div></div>' +
|
||||
'</div>' +
|
||||
'<h2 style="margin-bottom:20px;color:#4ade80">Membres de l\'equipe de genese</h2>';
|
||||
|
||||
if (g.team && g.team.length > 0) {
|
||||
g.team.forEach(function(m) {
|
||||
h += '<div class="member-card clickable" data-member-id="' + m.member + '">' +
|
||||
'<div class="member-header">' +
|
||||
'<div><div class="member-name">' + (m.fullName || m.member) + '</div>' +
|
||||
'<div style="font-size:12px;color:#a0a0a0;margin-top:4px">' + (m.role || '') + ' - ' + m.seniority + ' - ' + m.coverage + ' technologie(s)</div>' +
|
||||
'</div>' +
|
||||
'<div class="member-avail">' + m.availability + '% dispo</div>' +
|
||||
'</div>' +
|
||||
'<div class="tech-list">';
|
||||
if (m.technologies) {
|
||||
m.technologies.forEach(function(t) {
|
||||
h += '<span class="tech-tag">' + t.title + '</span>';
|
||||
});
|
||||
}
|
||||
h += '</div></div>';
|
||||
});
|
||||
} else {
|
||||
h += '<p style="color:#a0a0a0">Aucun membre ne repond aux criteres.</p>';
|
||||
}
|
||||
|
||||
if (g.uncoveredTechnologies && g.uncoveredTechnologies.length > 0) {
|
||||
h += '<div class="warning-box"><div class="warning-title">Technologies Adopt non couvertes</div><p style="margin-bottom:10px">Ces technologies critiques ne sont pas maitrisees :</p>';
|
||||
g.uncoveredTechnologies.forEach(function(t) {
|
||||
h += '<div class="uncovered"><strong>' + t.title + '</strong><div style="font-size:12px;color:#a0a0a0;margin-top:4px">Impact: ' + t.businessImpact + ' - Gap: ' + t.skillGap + ' - Couverture: ' + t.teamCoverage + ' personne(s)</div></div>';
|
||||
});
|
||||
h += '</div>';
|
||||
}
|
||||
|
||||
document.getElementById('genesis-team').innerHTML = h;
|
||||
|
||||
// Ajouter les evenements de clic sur les cartes membres
|
||||
document.querySelectorAll('.member-card.clickable').forEach(function(card) {
|
||||
card.addEventListener('click', function() {
|
||||
var memberId = this.getAttribute('data-member-id');
|
||||
if (memberId && window.__memberProfiles && window.__memberProfiles[memberId]) {
|
||||
showMemberProfile(memberId, window.__memberProfiles[memberId]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('EQUIPE: Equipe de genese initialisee');
|
||||
}
|
||||
|
||||
// Afficher la carte de profil d'un membre
|
||||
function showMemberProfile(memberId, profile) {
|
||||
if (!profile) {
|
||||
console.warn('EQUIPE: Profil non trouve pour', memberId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generer le HTML des competences
|
||||
var skillsHtml = '';
|
||||
if (profile.skillsDetailed && profile.skillsDetailed.length > 0) {
|
||||
skillsHtml = profile.skillsDetailed.map(function(s) {
|
||||
var levelClass = s.level || 'beginner';
|
||||
var yearsText = s.years ? ' (' + s.years + ' ans)' : '';
|
||||
return '<span class="skill-tag ' + levelClass + '">' + s.name + yearsText + '</span>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Generer le HTML des interets
|
||||
var interestsHtml = '';
|
||||
if (profile.interests && profile.interests.length > 0) {
|
||||
interestsHtml = profile.interests.map(function(i) {
|
||||
return '<span class="interest">' + i + '</span>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Generer le HTML des projets
|
||||
var projectsHtml = '';
|
||||
if (profile.projects && profile.projects.length > 0) {
|
||||
projectsHtml = '<ul class="projects">' + profile.projects.map(function(p) {
|
||||
return '<li>' + p + '</li>';
|
||||
}).join('') + '</ul>';
|
||||
}
|
||||
|
||||
// Generer le HTML des soft skills
|
||||
var softSkillsHtml = '';
|
||||
if (profile.softSkills && profile.softSkills.length > 0) {
|
||||
softSkillsHtml = profile.softSkills.map(function(s) {
|
||||
return '<span class="interest">' + s + '</span>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'profile-modal';
|
||||
modal.innerHTML =
|
||||
'<div class="profile-card">' +
|
||||
'<button class="close-btn">×</button>' +
|
||||
'<h2>' + (profile.fullName || memberId) + '</h2>' +
|
||||
'<p class="role">' + (profile.role || 'Membre de l\'equipe') + '</p>' +
|
||||
'<div class="stats">' +
|
||||
'<span class="stat"><strong>' + (profile.availability || 0) + '%</strong> disponibilite</span>' +
|
||||
'<span class="stat"><strong>' + (profile.yearsExperience || 0) + '</strong> ans exp.</span>' +
|
||||
'<span class="stat"><strong>' + (profile.seniorityLevel || 'beginner') + '</strong></span>' +
|
||||
(profile.joinDate ? '<span class="stat">Depuis <strong>' + profile.joinDate + '</strong></span>' : '') +
|
||||
'</div>' +
|
||||
(skillsHtml ? '<h3>Competences techniques</h3><div class="skills">' + skillsHtml + '</div>' : '') +
|
||||
(interestsHtml ? '<h3>Centres d\'interet</h3><div class="interests">' + interestsHtml + '</div>' : '') +
|
||||
(softSkillsHtml ? '<h3>Soft Skills</h3><div class="interests">' + softSkillsHtml + '</div>' : '') +
|
||||
(projectsHtml ? '<h3>Projets</h3>' + projectsHtml : '') +
|
||||
(profile.bio ? '<p class="bio">' + profile.bio + '</p>' : '') +
|
||||
'</div>';
|
||||
|
||||
// Fermer au clic sur le fond ou le bouton
|
||||
modal.addEventListener('click', function(e) {
|
||||
if (e.target === modal || e.target.classList.contains('close-btn')) {
|
||||
modal.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Fermer avec Echap
|
||||
var escHandler = function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
modal.remove();
|
||||
document.removeEventListener('keydown', escHandler);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', escHandler);
|
||||
|
||||
document.body.appendChild(modal);
|
||||
console.log('EQUIPE: Profil affiche pour', memberId);
|
||||
}
|
||||
|
||||
// Demarrer quand le DOM est pret
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', checkAndInitTeamPage);
|
||||
} else {
|
||||
checkAndInitTeamPage();
|
||||
}
|
||||
|
||||
// Detecter les navigations Next.js (SPA)
|
||||
var lastUrl = location.href;
|
||||
new MutationObserver(function() {
|
||||
if (location.href !== lastUrl) {
|
||||
lastUrl = location.href;
|
||||
window.__teamPageInitialized = false;
|
||||
checkAndInitTeamPage();
|
||||
}
|
||||
}).observe(document, { subtree: true, childList: true });
|
||||
})();
|
||||
2740
public/team-visualization-data.json
Normal file
2740
public/team-visualization-data.json
Normal file
File diff suppressed because it is too large
Load Diff
11
public/team/index.html
Normal file
11
public/team/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Equipe & Technologies - Laplank</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/team-block-script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
6
radar-app/.eslintrc.json
Normal file
6
radar-app/.eslintrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals",
|
||||
"rules": {
|
||||
"@next/next/no-img-element": "off"
|
||||
}
|
||||
}
|
||||
27
radar-app/.github/workflows/main.yml
vendored
Normal file
27
radar-app/.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Test AOE Technology Radar
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm
|
||||
- run: npm ci
|
||||
- run: npm run build:data
|
||||
- run: npm run build
|
||||
- run: if [ -n "$(git status --porcelain)" ]; then echo 'workspace is dirty after rebuilding' ; git status ; git diff ; exit 1 ; fi
|
||||
22
radar-app/.github/workflows/semanticore.yml
vendored
Normal file
22
radar-app/.github/workflows/semanticore.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Semanticore
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
semanticore:
|
||||
runs-on: ubuntu-latest
|
||||
name: Semanticore
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.*
|
||||
- name: Semanticore
|
||||
run: go run github.com/aoepeople/semanticore@v0 -npm-update-version package.json
|
||||
env:
|
||||
SEMANTICORE_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
42
radar-app/.gitignore
vendored
Normal file
42
radar-app/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
/techradar/
|
||||
|
||||
# generated
|
||||
/src/components/Icons/
|
||||
|
||||
/data/about.json
|
||||
/data/data.json
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.idea/
|
||||
*.pem
|
||||
*.tgz
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
1
radar-app/.husky/commit-msg
Executable file
1
radar-app/.husky/commit-msg
Executable file
@@ -0,0 +1 @@
|
||||
npx --no-install commitlint --edit "$1"
|
||||
1
radar-app/.husky/pre-commit
Normal file
1
radar-app/.husky/pre-commit
Normal file
@@ -0,0 +1 @@
|
||||
npx lint-staged
|
||||
8
radar-app/.npmignore
Normal file
8
radar-app/.npmignore
Normal file
@@ -0,0 +1,8 @@
|
||||
*.tgz
|
||||
.idea
|
||||
.github
|
||||
/.next/
|
||||
/out/
|
||||
/techradar/
|
||||
/data/about.json
|
||||
/data/data.json
|
||||
3
radar-app/.prettierignore
Normal file
3
radar-app/.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
out
|
||||
CHANGELOG.md
|
||||
11
radar-app/.prettierrc
Normal file
11
radar-app/.prettierrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": false,
|
||||
"semi": true,
|
||||
"importOrder": ["^[./]", "^@(.*)$"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"]
|
||||
}
|
||||
204
radar-app/LICENSE
Normal file
204
radar-app/LICENSE
Normal file
@@ -0,0 +1,204 @@
|
||||
The license applies to the generator code and not the articles in the "radar" folder. (Read also the README.md in this folder)
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
167
radar-app/bin/techradar.js
Normal file
167
radar-app/bin/techradar.js
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { execSync } = require("child_process");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const CWD = process.cwd();
|
||||
const BUILDER_DIR = path.join(CWD, ".techradar");
|
||||
const SOURCE_DIR = path.join(CWD, "node_modules", "aoe_technology_radar");
|
||||
const HASH_FILE = path.join(BUILDER_DIR, "hash");
|
||||
|
||||
const PARAMETER = process.argv[2]; // "build" or "serve"
|
||||
const FLAGS = process.argv.slice(3).join(" ");
|
||||
|
||||
function info(message) {
|
||||
console.log(`\x1b[32m${message}\x1b[0m`);
|
||||
}
|
||||
|
||||
function warn(message) {
|
||||
console.log(`\x1b[33mWarning: ${message}\x1b[0m`);
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
console.error(`Error: ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function bootstrap() {
|
||||
if (!fs.existsSync(path.join(CWD, "radar"))) {
|
||||
warn(
|
||||
"Could not find radar directory. Created a bootstrap radar directory in your current working directory. Feel free to customize it.",
|
||||
);
|
||||
fs.cpSync(path.join(SOURCE_DIR, "data", "radar"), path.join(CWD, "radar"), {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(CWD, "public"))) {
|
||||
warn(
|
||||
"Could not find public directory. Created a public radar directory in your current working directory.",
|
||||
);
|
||||
fs.cpSync(path.join(SOURCE_DIR, "public"), path.join(CWD, "public"), {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(CWD, "config.json"))) {
|
||||
warn(
|
||||
"Could not find a config.json. Created a bootstrap config.json in your current working directory. Customize it to your needs.",
|
||||
);
|
||||
fs.copyFileSync(
|
||||
path.join(SOURCE_DIR, "data", "config.default.json"),
|
||||
path.join(CWD, "config.json"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(CWD, "about.md"))) {
|
||||
warn(
|
||||
"Could not find a about.md. Created a bootstrap about.md in your current working directory. Customize it to your needs.",
|
||||
);
|
||||
fs.copyFileSync(
|
||||
path.join(SOURCE_DIR, "data", "about.md"),
|
||||
path.join(CWD, "about.md"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(CWD, "custom.css"))) {
|
||||
warn("Created a bootstrap custom.css in your current working directory.");
|
||||
fs.copyFileSync(
|
||||
path.join(SOURCE_DIR, "src", "styles", "custom.css"),
|
||||
path.join(CWD, "custom.css"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate current hash of package.json
|
||||
function calculateHash(file) {
|
||||
const fileBuffer = fs.readFileSync(file);
|
||||
const hashSum = crypto.createHash("sha256");
|
||||
hashSum.update(fileBuffer);
|
||||
return hashSum.digest("hex");
|
||||
}
|
||||
|
||||
const CURRENT_HASH = calculateHash(path.join(CWD, "package.json"));
|
||||
|
||||
// Check if builder dir needs to be recreated
|
||||
let RECREATE_DIR = false;
|
||||
if (
|
||||
!fs.existsSync(BUILDER_DIR) ||
|
||||
!fs.existsSync(HASH_FILE) ||
|
||||
fs.readFileSync(HASH_FILE, "utf8") !== CURRENT_HASH
|
||||
) {
|
||||
RECREATE_DIR = true;
|
||||
}
|
||||
|
||||
if (RECREATE_DIR) {
|
||||
// Remove existing builder dir if it exists
|
||||
if (fs.existsSync(BUILDER_DIR)) {
|
||||
fs.rmSync(BUILDER_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Copy source dir to builder dir
|
||||
try {
|
||||
fs.cpSync(SOURCE_DIR, BUILDER_DIR, { recursive: true });
|
||||
fs.writeFileSync(HASH_FILE, CURRENT_HASH);
|
||||
} catch (e) {
|
||||
error(`Could not copy ${SOURCE_DIR} to ${BUILDER_DIR}`);
|
||||
}
|
||||
|
||||
try {
|
||||
process.chdir(BUILDER_DIR);
|
||||
info("Installing npm packages");
|
||||
execSync("npm install", { stdio: "inherit" });
|
||||
} catch (e) {
|
||||
error("Could not install npm packages");
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
try {
|
||||
if (fs.existsSync(path.join(BUILDER_DIR, "data", "radar"))) {
|
||||
fs.rmSync(path.join(BUILDER_DIR, "data", "radar"), { recursive: true });
|
||||
}
|
||||
fs.cpSync(path.join(CWD, "radar"), path.join(BUILDER_DIR, "data", "radar"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.cpSync(path.join(CWD, "public"), path.join(BUILDER_DIR, "public"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.copyFileSync(
|
||||
path.join(CWD, "about.md"),
|
||||
path.join(BUILDER_DIR, "data", "about.md"),
|
||||
);
|
||||
fs.copyFileSync(
|
||||
path.join(CWD, "custom.css"),
|
||||
path.join(BUILDER_DIR, "src", "styles", "custom.css"),
|
||||
);
|
||||
fs.copyFileSync(
|
||||
path.join(CWD, "config.json"),
|
||||
path.join(BUILDER_DIR, "data", "config.json"),
|
||||
);
|
||||
process.chdir(BUILDER_DIR);
|
||||
} catch (e) {
|
||||
error(e.message);
|
||||
}
|
||||
|
||||
info("Building data");
|
||||
execSync(`npm run build:data -- ${FLAGS}`, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (PARAMETER === "serve") {
|
||||
info("Starting techradar");
|
||||
execSync("npm run dev", { stdio: "inherit" });
|
||||
}
|
||||
|
||||
if (PARAMETER === "build") {
|
||||
info("Building techradar");
|
||||
execSync("npm run build", { stdio: "inherit" });
|
||||
if (fs.existsSync(path.join(CWD, "build"))) {
|
||||
fs.rmSync(path.join(CWD, "build"), { recursive: true });
|
||||
}
|
||||
info(`Copying techradar to ${path.join(CWD, "build")}`);
|
||||
fs.renameSync(path.join(BUILDER_DIR, "out"), path.join(CWD, "build"));
|
||||
}
|
||||
26
radar-app/commitlint.config.js
Normal file
26
radar-app/commitlint.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
rules: {
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"feat",
|
||||
"sec",
|
||||
"fix",
|
||||
"bug",
|
||||
"test",
|
||||
"refactor",
|
||||
"rework",
|
||||
"ops",
|
||||
"ci",
|
||||
"cd",
|
||||
"build",
|
||||
"doc",
|
||||
"perf",
|
||||
"chore",
|
||||
"update",
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
147
radar-app/data/config.default.json
Normal file
147
radar-app/data/config.default.json
Normal file
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"basePath": "/techradar",
|
||||
"baseUrl": "",
|
||||
"editUrl": "https://github.dev/AOEpeople/techradar/blob/main/radar/{release}/{id}.md",
|
||||
"logoFile": "logo.svg",
|
||||
"jsFile": "",
|
||||
"toggles": {
|
||||
"showSearch": false,
|
||||
"showChart": true,
|
||||
"showTagFilter": true,
|
||||
"showQuadrantList": true,
|
||||
"showEmptyRings": false
|
||||
},
|
||||
"sections": ["radar", "tags", "list"],
|
||||
"colors": {
|
||||
"foreground": "#fcf2e6",
|
||||
"background": "#113521",
|
||||
"highlight": "#d4a373",
|
||||
"content": "#fff",
|
||||
"text": "#575757",
|
||||
"link": "#bc6c25",
|
||||
"border": "rgba(255, 255, 255, 0.1)",
|
||||
"tag": "rgba(255, 255, 255, 0.1)"
|
||||
},
|
||||
"quadrants": [
|
||||
{
|
||||
"id": "languages-and-frameworks",
|
||||
"title": "Languages & Frameworks",
|
||||
"description": "A selection of programming languages, alongside essential frameworks for building a variety of custom software.",
|
||||
"color": "#a3b18a"
|
||||
},
|
||||
{
|
||||
"id": "methods-and-patterns",
|
||||
"title": "Methods & Patterns",
|
||||
"description": "Key software development methods and design patterns, covering everything from continuous integration and testing to architecture.",
|
||||
"color": "#588157"
|
||||
},
|
||||
{
|
||||
"id": "platforms-and-operations",
|
||||
"title": "Platforms & Operations",
|
||||
"description": "Technologies and tools for software and infrastructure operations, including platforms and services for managing and scaling applications.",
|
||||
"color": "#3f633e"
|
||||
},
|
||||
{
|
||||
"id": "tools",
|
||||
"title": "Tools",
|
||||
"description": "A range of software tools, from simple productivity enhancers to comprehensive project solutions, catering to various project needs.",
|
||||
"color": "#40713f"
|
||||
}
|
||||
],
|
||||
"rings": [
|
||||
{
|
||||
"id": "adopt",
|
||||
"title": "Adopt",
|
||||
"description": "",
|
||||
"color": "#588157",
|
||||
"radius": 0.5,
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"id": "trial",
|
||||
"title": "Trial",
|
||||
"description": "",
|
||||
"color": "#457b9d",
|
||||
"radius": 0.69,
|
||||
"strokeWidth": 3
|
||||
},
|
||||
{
|
||||
"id": "assess",
|
||||
"title": "Assess",
|
||||
"description": "",
|
||||
"color": "#bc6c25",
|
||||
"radius": 0.85,
|
||||
"strokeWidth": 2
|
||||
},
|
||||
{
|
||||
"id": "hold",
|
||||
"title": "Hold",
|
||||
"description": "",
|
||||
"color": "#d62828",
|
||||
"radius": 1,
|
||||
"strokeWidth": 0.75
|
||||
}
|
||||
],
|
||||
"flags": {
|
||||
"new": {
|
||||
"color": "#f1235a",
|
||||
"title": "New",
|
||||
"titleShort": "N",
|
||||
"description": "New in this version"
|
||||
},
|
||||
"changed": {
|
||||
"color": "#40a7d1",
|
||||
"title": "Changed",
|
||||
"titleShort": "C",
|
||||
"description": "Recently changed"
|
||||
},
|
||||
"default": {
|
||||
"description": "Unchanged"
|
||||
}
|
||||
},
|
||||
"chart": {
|
||||
"size": 800,
|
||||
"blipSize": 12
|
||||
},
|
||||
"social": [
|
||||
{
|
||||
"href": "https://twitter.com/aoepeople",
|
||||
"icon": "x"
|
||||
},
|
||||
{
|
||||
"href": "https://www.linkedin.com/company/aoe",
|
||||
"icon": "linkedIn"
|
||||
},
|
||||
{
|
||||
"href": "https://www.xing.com/company/aoe",
|
||||
"icon": "xing"
|
||||
},
|
||||
{
|
||||
"href": "https://github.com/aoepeople",
|
||||
"icon": "github"
|
||||
}
|
||||
],
|
||||
"imprint": "https://www.aoe.com/en/imprint.html",
|
||||
"labels": {
|
||||
"title": "Technology Radar",
|
||||
"imprint": "Legal Information",
|
||||
"quadrant": "Quadrant",
|
||||
"quadrantOverview": "Quadrant Overview",
|
||||
"zoomIn": "Zoom in",
|
||||
"filterByTag": "Filter by Tag",
|
||||
"footer": "The technology radar is a project by AOE GmbH. Feel free to build your own radar based on the open source project.",
|
||||
"notUpdated": "This item was not updated in last three versions of the Radar. Should it have appeared in one of the more recent editions, there is a good chance it remains pertinent. However, if the item dates back further, its relevance may have diminished and our current evaluation could vary. Regrettably, our capacity to consistently revisit items from past Radar editions is limited.",
|
||||
"notFound": "404 - Page not found",
|
||||
"pageAbout": "How to use AOE Technology Radar?",
|
||||
"pageOverview": "Technologies Overview",
|
||||
"pageSearch": "Search",
|
||||
"searchPlaceholder": "What are you looking for?",
|
||||
"metaDescription": ""
|
||||
},
|
||||
"fuzzySearch": {
|
||||
"threshold": 0.4,
|
||||
"distance": 600,
|
||||
"ignoreLocation": false,
|
||||
"includeScore": true
|
||||
}
|
||||
}
|
||||
3
radar-app/data/config.json
Normal file
3
radar-app/data/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"basePath": "/techradar"
|
||||
}
|
||||
BIN
radar-app/docs/assets/screenshot-techradar.png
Normal file
BIN
radar-app/docs/assets/screenshot-techradar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 283 KiB |
16
radar-app/next.config.js
Normal file
16
radar-app/next.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const config = require("./data/config.json");
|
||||
const basePath =
|
||||
config.basePath && config.basePath !== "/" ? config.basePath : "";
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const nextConfig = {
|
||||
basePath,
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
10393
radar-app/package-lock.json
generated
Normal file
10393
radar-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
radar-app/package.json
Normal file
48
radar-app/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "aoe_technology_radar",
|
||||
"version": "4.7.0-rc.1",
|
||||
"bin": {
|
||||
"techradar": "bin/techradar.js"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build:icons": "npx @svgr/cli --typescript --no-dimensions --no-prettier --out-dir src/components/Icons -- src/icons",
|
||||
"build:data": "tsx scripts/buildData.ts",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"fix": "prettier . --write",
|
||||
"prepare": "husky",
|
||||
"postinstall": "npm run build:icons"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.8.0",
|
||||
"@commitlint/config-conventional": "^19.8.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-config-next": "16.1.6",
|
||||
"fuse.js": "^7.1.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"highlight.js": "^11.11.1",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.5.0",
|
||||
"marked": "^15.0.7",
|
||||
"marked-highlight": "^2.2.1",
|
||||
"next": "16.1.6",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"postcss-preset-env": "^10.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"tsx": "^4.19.3",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*": "prettier --write --ignore-unknown"
|
||||
}
|
||||
}
|
||||
17
radar-app/postcss.config.js
Normal file
17
radar-app/postcss.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
"postcss-nested",
|
||||
[
|
||||
"postcss-preset-env",
|
||||
{
|
||||
autoprefixer: {
|
||||
flexbox: "no-2009",
|
||||
},
|
||||
stage: 3,
|
||||
features: {
|
||||
"custom-properties": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
BIN
radar-app/public/favicon.ico
Normal file
BIN
radar-app/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
radar-app/public/logo.svg
Normal file
1
radar-app/public/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 149.6 64.73"><path d="M25.87,0h-1.77v7.24C10.65,8.15,0,19.35,0,33.02s11.62,25.87,25.87,25.87,24.87-10.65,25.79-24.1h7.24v-1.77C58.89,14.81,44.09,0,25.87,0ZM25.87,55.32c-12.31,0-22.32-10.01-22.32-22.32S12.59,11.67,24.07,10.76v24.01h24.01c-.92,11.48-10.54,20.52-22.24,20.52h0l.03.03ZM51.74,31.22h-24.1V3.63c14.81.89,26.7,12.78,27.59,27.62h-3.52l.03-.03Z" fill="#fff" fill-rule="evenodd" stroke-width="0"/><path d="M78.09,10.41h-6.09v16.5h-1.5V10.41h-6.08v-1.27h13.67v1.27Z" fill="#fff" stroke-width="0"/><path d="M90.78,18.39h-8.31v7.25h9.56v1.27h-11.06V9.14h11v1.27h-9.5v6.71h8.31v1.27Z" fill="#fff" stroke-width="0"/><path d="M107.77,21.37c-.2,1.87-.87,3.3-2.01,4.3-1.13.99-2.65,1.49-4.53,1.49-1.32,0-2.48-.33-3.5-.99-1.01-.66-1.8-1.59-2.35-2.8-.55-1.21-.83-2.59-.84-4.14v-2.31c0-1.58.28-2.98.83-4.2.55-1.22,1.35-2.16,2.39-2.83,1.04-.66,2.23-1,3.58-1,1.9,0,3.41.51,4.51,1.54,1.1,1.03,1.74,2.45,1.92,4.26h-1.51c-.38-3.02-2.01-4.53-4.92-4.53-1.61,0-2.9.6-3.85,1.81s-1.43,2.87-1.43,5v2.17c0,2.05.46,3.69,1.4,4.91.93,1.22,2.19,1.83,3.78,1.83s2.75-.38,3.55-1.13c.8-.75,1.29-1.88,1.48-3.39h1.51Z" fill="#fff" stroke-width="0"/><path d="M124.59,26.91h-1.51v-8.52h-10.16v8.52h-1.5V9.14h1.5v7.98h10.16v-7.98h1.51v17.77Z" fill="#fff" stroke-width="0"/><path d="M71.73,41.62h-2.32v6.29h-4.28v-17.77h6.99c2.11,0,3.76.47,4.94,1.4,1.19.93,1.78,2.26,1.78,3.96,0,1.24-.25,2.26-.75,3.07-.5.81-1.28,1.47-2.35,1.98l3.71,7.18v.18h-4.59l-3.14-6.29ZM69.41,38.33h2.71c.81,0,1.43-.21,1.84-.64.41-.43.62-1.03.62-1.79s-.21-1.37-.62-1.81-1.03-.65-1.83-.65h-2.71v4.9Z" fill="#fff" stroke-width="0"/><path d="M91.53,44.59h-5.87l-1.03,3.32h-4.58l6.52-17.77h4.03l6.57,17.77h-4.6l-1.04-3.32ZM86.69,41.28h3.82l-1.92-6.16-1.9,6.16Z" fill="#fff" stroke-width="0"/><path d="M98.47,47.91v-17.77h5.73c1.57,0,2.98.36,4.24,1.07,1.25.71,2.23,1.72,2.94,3.01s1.06,2.75,1.07,4.36v.82c0,1.63-.34,3.09-1.03,4.38-.69,1.29-1.66,2.3-2.91,3.03-1.25.73-2.64,1.1-4.18,1.1h-5.85ZM102.75,33.44v11.17h1.49c1.23,0,2.17-.44,2.83-1.31.66-.87.99-2.17.99-3.9v-.77c0-1.72-.33-3.01-.99-3.88-.66-.87-1.62-1.31-2.88-1.31h-1.44Z" fill="#fff" stroke-width="0"/><path d="M124.47,44.59h-5.87l-1.03,3.32h-4.58l6.52-17.77h4.03l6.57,17.77h-4.6l-1.04-3.32ZM119.62,41.28h3.82l-1.92-6.16-1.9,6.16Z" fill="#fff" stroke-width="0"/><path d="M138.01,41.62h-2.32v6.29h-4.28v-17.77h7c2.11,0,3.75.47,4.94,1.4,1.19.93,1.78,2.26,1.78,3.96,0,1.24-.25,2.26-.75,3.07-.5.81-1.28,1.47-2.35,1.98l3.71,7.18v.18h-4.59l-3.14-6.29ZM135.69,38.33h2.71c.81,0,1.43-.21,1.84-.64.41-.43.62-1.03.62-1.79s-.21-1.37-.62-1.81c-.41-.44-1.03-.65-1.83-.65h-2.71v4.9Z" fill="#fff" stroke-width="0"/></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
3
radar-app/renovate.json
Normal file
3
radar-app/renovate.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["config:base", ":semanticCommitTypeAll(chore)"]
|
||||
}
|
||||
271
radar-app/scripts/buildData.ts
Normal file
271
radar-app/scripts/buildData.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import fs from "fs";
|
||||
import matter from "gray-matter";
|
||||
import hljs from "highlight.js";
|
||||
import { Marked } from "marked";
|
||||
import { markedHighlight } from "marked-highlight";
|
||||
import path from "path";
|
||||
|
||||
import nextConfig from "../next.config.js";
|
||||
import config from "../src/lib/config";
|
||||
import ErrorHandler, { ErrorType, TextColor } from "./errorHandler.js";
|
||||
import Positioner from "./positioner";
|
||||
|
||||
import { Flag, Item } from "@/lib/types";
|
||||
|
||||
const {
|
||||
rings,
|
||||
chart: { size },
|
||||
} = config;
|
||||
|
||||
const ringIds = rings.map((r) => r.id);
|
||||
const quadrants = config.quadrants.map((q, i) => ({ ...q, position: i + 1 }));
|
||||
const quadrantIds = quadrants.map((q) => q.id);
|
||||
const tags = (config as { tags?: string[] }).tags || [];
|
||||
const positioner = new Positioner(size, quadrants, rings);
|
||||
const errorHandler = new ErrorHandler(quadrants, rings);
|
||||
|
||||
const marked = new Marked(
|
||||
markedHighlight({
|
||||
langPrefix: "hljs language-",
|
||||
highlight(code, lang, info) {
|
||||
const language = hljs.getLanguage(lang) ? lang : "plaintext";
|
||||
return hljs.highlight(code, { language }).value;
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
function dataPath(...paths: string[]): string {
|
||||
return path.resolve("data", ...paths);
|
||||
}
|
||||
|
||||
function convertToHtml(markdown: string): string {
|
||||
// replace deprecated internal links with .html extension
|
||||
markdown = markdown.replace(/(]\(\/[^)]+)\.html/g, "$1/");
|
||||
|
||||
if (nextConfig.basePath) {
|
||||
markdown = markdown.replace(/]\(\//g, `](${nextConfig.basePath}/`);
|
||||
}
|
||||
|
||||
let html = marked.parse(markdown.trim()) as string;
|
||||
html = html.replace(
|
||||
/a href="http/g,
|
||||
'a target="_blank" rel="noopener noreferrer" href="http',
|
||||
);
|
||||
return html;
|
||||
}
|
||||
|
||||
function readMarkdownFile(filePath: string) {
|
||||
const id = path.basename(filePath, ".md");
|
||||
const fileContent = fs.readFileSync(filePath, "utf8");
|
||||
|
||||
try {
|
||||
const { data, content } = matter(fileContent);
|
||||
const body = convertToHtml(content);
|
||||
return { id, data, body };
|
||||
} catch (error) {
|
||||
console.error(`Failed parsing ${filePath}: ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to recursively read Markdown files and parse them
|
||||
async function parseDirectory(dirPath: string): Promise<Item[]> {
|
||||
const items: Record<string, Item> = {};
|
||||
|
||||
async function readDir(dirPath: string) {
|
||||
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dirPath, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await readDir(fullPath);
|
||||
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
||||
const releaseDate = path.basename(path.dirname(fullPath));
|
||||
const { id, data, body } = readMarkdownFile(fullPath);
|
||||
|
||||
if (!items[id]) {
|
||||
items[id] = {
|
||||
id,
|
||||
release: releaseDate,
|
||||
title: data.title || id,
|
||||
ring: data.ring,
|
||||
quadrant: data.quadrant,
|
||||
body,
|
||||
featured: data.featured !== false,
|
||||
flag: Flag.Default,
|
||||
tags: data.tags || [],
|
||||
revisions: [],
|
||||
position: [0, 0],
|
||||
};
|
||||
} else {
|
||||
items[id].release = releaseDate;
|
||||
items[id].body = body || items[id].body;
|
||||
items[id].title = data.title || items[id].title;
|
||||
items[id].ring = data.ring || items[id].ring;
|
||||
items[id].quadrant = data.quadrant || items[id].quadrant;
|
||||
items[id].tags = data.tags || items[id].tags;
|
||||
items[id].featured =
|
||||
typeof data.featured === "boolean"
|
||||
? data.featured
|
||||
: items[id].featured;
|
||||
}
|
||||
|
||||
items[id].revisions!.push({
|
||||
release: releaseDate,
|
||||
ring: data.ring,
|
||||
body,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await readDir(dirPath);
|
||||
return Object.values(items).sort((a, b) => a.title.localeCompare(b.title));
|
||||
}
|
||||
|
||||
function getUniqueReleases(items: Item[]): string[] {
|
||||
const releases = new Set<string>();
|
||||
for (const item of items) {
|
||||
for (const revision of item.revisions || []) {
|
||||
releases.add(revision.release);
|
||||
}
|
||||
}
|
||||
return Array.from(releases).sort();
|
||||
}
|
||||
|
||||
function getUniqueTags(items: Item[]): string[] {
|
||||
const tags = new Set<string>();
|
||||
for (const item of items) {
|
||||
for (const tag of item.tags || []) {
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
return Array.from(tags).sort();
|
||||
}
|
||||
|
||||
function getFlag(item: Item, allReleases: string[]): Flag {
|
||||
// return default flag if this is the first edition of the radar
|
||||
if (allReleases.length === 1) {
|
||||
return Flag.Default;
|
||||
}
|
||||
|
||||
const latestRelease = allReleases[allReleases.length - 1];
|
||||
const revisions = item.revisions || [];
|
||||
const isInLatestRelease =
|
||||
revisions.length > 0 &&
|
||||
revisions[revisions.length - 1].release === latestRelease;
|
||||
|
||||
if (revisions.length == 1 && isInLatestRelease) {
|
||||
return Flag.New;
|
||||
} else if (revisions.length > 1 && isInLatestRelease) {
|
||||
return Flag.Changed;
|
||||
}
|
||||
|
||||
return Flag.Default;
|
||||
}
|
||||
|
||||
function postProcessItems(items: Item[]): {
|
||||
releases: string[];
|
||||
tags: string[];
|
||||
items: Item[];
|
||||
} {
|
||||
const filteredItems = items.filter((item) => {
|
||||
// check if the items' quadrant and ring are valid
|
||||
if (!item.quadrant || !item.ring) {
|
||||
errorHandler.processBuildErrors(ErrorType.NoQuadrant, item.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!quadrantIds.includes(item.quadrant)) {
|
||||
errorHandler.processBuildErrors(
|
||||
ErrorType.InvalidQuadrant,
|
||||
item.id,
|
||||
item.quadrant,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ringIds.includes(item.ring)) {
|
||||
errorHandler.processBuildErrors(
|
||||
ErrorType.InvalidRing,
|
||||
item.id,
|
||||
item.ring,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if config has a key `tags` and if it is an array
|
||||
if (Array.isArray(tags) && tags.length) {
|
||||
// if tags are specified, only keep items that have at least one of the tags
|
||||
return item.tags?.some((tag) => tags.includes(tag));
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
errorHandler.checkForBuildErrors();
|
||||
|
||||
const releases = getUniqueReleases(filteredItems);
|
||||
const uniqueTags = getUniqueTags(filteredItems);
|
||||
const processedItems = filteredItems.map((item) => {
|
||||
const processedItem = {
|
||||
...item,
|
||||
position: positioner.getNextPosition(item.quadrant, item.ring),
|
||||
flag: getFlag(item, releases),
|
||||
// only keep revision which ring or body is different
|
||||
revisions: item.revisions
|
||||
?.filter((revision, index, revisions) => {
|
||||
const { ring, body } = revision;
|
||||
return (
|
||||
ring !== item.ring ||
|
||||
(body != "" &&
|
||||
body != item.body &&
|
||||
body !== revisions[index - 1]?.body)
|
||||
);
|
||||
})
|
||||
.reverse(),
|
||||
};
|
||||
|
||||
// unset revisions if there are none
|
||||
if (!processedItem.revisions?.length) {
|
||||
delete processedItem.revisions;
|
||||
}
|
||||
|
||||
// unset tags if there are none
|
||||
if (!processedItem.tags?.length) {
|
||||
delete processedItem.tags;
|
||||
}
|
||||
|
||||
return processedItem;
|
||||
});
|
||||
|
||||
return { releases, tags: uniqueTags, items: processedItems };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Parse the data and write radar data to JSON file
|
||||
const items = await parseDirectory(dataPath("radar"));
|
||||
const data = postProcessItems(items);
|
||||
|
||||
if (data.items.length === 0) {
|
||||
errorHandler.processBuildErrors(ErrorType.NoRadarItems);
|
||||
}
|
||||
|
||||
errorHandler.checkForBuildErrors(true);
|
||||
|
||||
const json = JSON.stringify(data, null, 2);
|
||||
fs.writeFileSync(dataPath("data.json"), json);
|
||||
|
||||
// write about data to JSON file
|
||||
const about = readMarkdownFile(dataPath("about.md"));
|
||||
fs.writeFileSync(dataPath("about.json"), JSON.stringify(about, null, 2));
|
||||
console.log(
|
||||
"ℹ️ Data written to data/data.json and data/about.json\n\n" +
|
||||
errorHandler.colorizeBackground(
|
||||
" Build was successfull ",
|
||||
TextColor.Green,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
108
radar-app/scripts/errorHandler.ts
Normal file
108
radar-app/scripts/errorHandler.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Quadrant, Ring } from "@/lib/types";
|
||||
|
||||
export enum ErrorType {
|
||||
NoQuadrant = "Item {0} has no quadrant or ring",
|
||||
InvalidQuadrant = "Item {0} has invalid quadrant {1}\n\tvalid quadrants are: {2}",
|
||||
InvalidRing = "Item {0} has invalid ring {1}\n\tvalid rings are: {2}",
|
||||
NoRadarItems = "No valid radar items found. Please check the markdown files in the `radar` directory.",
|
||||
}
|
||||
|
||||
export enum TextColor {
|
||||
Default = 0,
|
||||
Black,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Mangenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
}
|
||||
|
||||
export default class ErrorHandler {
|
||||
private buildErrors: string[] = [];
|
||||
private quadrants: Quadrant[];
|
||||
private rings: Ring[];
|
||||
private isStrict: boolean;
|
||||
private supportsColor: boolean;
|
||||
|
||||
constructor(quadrants: Quadrant[], rings: Ring[]) {
|
||||
this.isStrict = process.argv.slice(2).includes("--strict");
|
||||
this.supportsColor = process.stdout.isTTY && process.env.TERM !== "dumb";
|
||||
this.quadrants = quadrants;
|
||||
this.rings = rings;
|
||||
console.log(`ℹ️ Build is${this.isStrict ? "" : " not"} in strict mode\n`);
|
||||
}
|
||||
|
||||
public processBuildErrors(errorType: ErrorType, ...args: string[]) {
|
||||
const errorHint = this.getErrorHint(errorType);
|
||||
const errorMsg = this.formatString(
|
||||
errorType.toString(),
|
||||
errorHint ? [...args, errorHint] : args,
|
||||
);
|
||||
this.buildErrors.push(errorMsg);
|
||||
}
|
||||
|
||||
public checkForBuildErrors(exitOnErr: boolean = false) {
|
||||
if (this.buildErrors.length > 0) {
|
||||
console.warn(
|
||||
this.colorizeBackground(
|
||||
`There ${this.buildErrors.length > 1 ? "are" : "is"} ${this.buildErrors.length} error${this.buildErrors.length > 1 ? "s" : ""} in your data build`,
|
||||
TextColor.Red,
|
||||
) +
|
||||
"\n\n" +
|
||||
this.buildErrors
|
||||
.map((error, index) => `${index + 1}. ${error}`)
|
||||
.join("\n") +
|
||||
"\n",
|
||||
);
|
||||
|
||||
if (this.isStrict || exitOnErr) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
this.buildErrors = [];
|
||||
}
|
||||
}
|
||||
|
||||
private getErrorHint(errorType: ErrorType) {
|
||||
switch (errorType) {
|
||||
case ErrorType.InvalidQuadrant:
|
||||
return this.quadrants.map((quadrant) => quadrant.id).join(", ");
|
||||
case ErrorType.InvalidRing:
|
||||
return this.rings.map((ring) => ring.id).join(", ");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public colorizeBackground(str: string, backgroundColor: TextColor) {
|
||||
if (this.supportsColor) {
|
||||
return `\x1b[${backgroundColor + 10}m${str}\x1b[${TextColor.Default}m`;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private formatString(msg: string, inserts: string[]) {
|
||||
return inserts.reduce(
|
||||
(acc, cur, index) =>
|
||||
acc.replaceAll(
|
||||
`{${index}}`,
|
||||
this.colorizeString(
|
||||
cur,
|
||||
index === 2 ? TextColor.Green : TextColor.Red,
|
||||
),
|
||||
),
|
||||
msg,
|
||||
);
|
||||
}
|
||||
|
||||
private colorizeString(str: string, textColor: TextColor) {
|
||||
if (this.supportsColor) {
|
||||
return `\x1b[${textColor}m${str}\x1b[${TextColor.Default}m`;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
95
radar-app/scripts/positioner.ts
Normal file
95
radar-app/scripts/positioner.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Quadrant, Ring } from "@/lib/types";
|
||||
|
||||
type Position = [x: number, y: number];
|
||||
type RingDimension = [innerRadius: number, outerRadius: number];
|
||||
|
||||
// Corresponding to positions 1, 2, 3, and 4 respectively
|
||||
const startAngles = [270, 0, 180, 90];
|
||||
|
||||
export default class Positioner {
|
||||
private readonly centerRadius: number;
|
||||
private readonly minDistance: number = 20;
|
||||
private readonly paddingRing: number = 15;
|
||||
private readonly paddingAngle: number = 10;
|
||||
private positions: Record<string, Position[]> = {};
|
||||
private ringDimensions: Record<string, RingDimension> = {};
|
||||
private quadrantAngles: Record<string, number> = {};
|
||||
|
||||
constructor(size: number, quadrants: Quadrant[], rings: Ring[]) {
|
||||
this.centerRadius = size / 2;
|
||||
|
||||
quadrants.forEach((quadrant) => {
|
||||
this.quadrantAngles[quadrant.id] = startAngles[quadrant.position - 1];
|
||||
});
|
||||
|
||||
rings.forEach((ring, index) => {
|
||||
const innerRadius =
|
||||
(rings[index - 1]?.radius ?? 0) * this.centerRadius + this.paddingRing;
|
||||
const outerRadius =
|
||||
(ring.radius ?? 1) * this.centerRadius - this.paddingRing;
|
||||
this.ringDimensions[ring.id] = [innerRadius, outerRadius];
|
||||
});
|
||||
}
|
||||
|
||||
static getDistance(a: Position, b: Position): number {
|
||||
const [x1, y1] = a;
|
||||
const [x2, y2] = b;
|
||||
return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
|
||||
}
|
||||
|
||||
private isOverlapping(position: Position, positions: Position[]): boolean {
|
||||
return positions.some(
|
||||
(p) => Positioner.getDistance(position, p) < this.minDistance,
|
||||
);
|
||||
}
|
||||
|
||||
private getXYPosition(
|
||||
quadrantId: string,
|
||||
ringId: string,
|
||||
radiusFraction: number,
|
||||
angleFraction: number,
|
||||
): Position {
|
||||
const [innerRadius, outerRadius] = this.ringDimensions[ringId];
|
||||
const ringWidth = outerRadius - innerRadius;
|
||||
const absoluteRadius = innerRadius + radiusFraction * ringWidth;
|
||||
|
||||
const startAngle = this.quadrantAngles[quadrantId] + this.paddingAngle;
|
||||
const endAngle = startAngle + 90 - 2 * this.paddingAngle;
|
||||
const absoluteAngle = startAngle + (endAngle - startAngle) * angleFraction;
|
||||
const angleInRadians = ((absoluteAngle - 90) * Math.PI) / 180;
|
||||
|
||||
return [
|
||||
Math.round(this.centerRadius + absoluteRadius * Math.cos(angleInRadians)),
|
||||
Math.round(this.centerRadius + absoluteRadius * Math.sin(angleInRadians)),
|
||||
];
|
||||
}
|
||||
|
||||
public getNextPosition(quadrantId: string, ringId: string): Position {
|
||||
this.positions[quadrantId] ??= [];
|
||||
|
||||
let tries = 0;
|
||||
let position: Position;
|
||||
|
||||
do {
|
||||
position = this.getXYPosition(
|
||||
quadrantId,
|
||||
ringId,
|
||||
Math.sqrt(Math.random()),
|
||||
Math.random(),
|
||||
);
|
||||
tries++;
|
||||
} while (
|
||||
this.isOverlapping(position, this.positions[quadrantId]) &&
|
||||
tries < 150
|
||||
);
|
||||
|
||||
if (tries >= 150) {
|
||||
console.warn(
|
||||
`Could not find a non-overlapping position for ${quadrantId} in ring ${ringId}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.positions[quadrantId].push(position);
|
||||
return position;
|
||||
}
|
||||
}
|
||||
40
radar-app/src/app/sitemap.ts
Normal file
40
radar-app/src/app/sitemap.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { MetadataRoute } from "next";
|
||||
|
||||
import { getAbsoluteUrl, getItems, getQuadrants } from "@/lib/data";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
export const revalidate = 60;
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const quadrants = getQuadrants().map((quadrant) => ({
|
||||
url: getAbsoluteUrl(`/${quadrant.id}/`),
|
||||
lastModified: new Date(),
|
||||
priority: 0.8,
|
||||
}));
|
||||
|
||||
const items = getItems().map((item) => ({
|
||||
url: getAbsoluteUrl(`/${item.quadrant}/${item.id}/`),
|
||||
lastModified: new Date(),
|
||||
priority: 0.5,
|
||||
}));
|
||||
|
||||
return [
|
||||
{
|
||||
url: getAbsoluteUrl(),
|
||||
lastModified: new Date(),
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
url: getAbsoluteUrl("/overview/"),
|
||||
lastModified: new Date(),
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: getAbsoluteUrl("/help-and-about-tech-radar/"),
|
||||
lastModified: new Date(),
|
||||
priority: 0.9,
|
||||
},
|
||||
...quadrants,
|
||||
...items,
|
||||
];
|
||||
}
|
||||
50
radar-app/src/components/Badge/Badge.module.css
Normal file
50
radar-app/src/components/Badge/Badge.module.css
Normal file
@@ -0,0 +1,50 @@
|
||||
.badge {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 6px 15px;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 13px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.size-small {
|
||||
padding: 4px 8px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.size-large {
|
||||
padding: 7px 20px;
|
||||
font-size: 14px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.colored {
|
||||
color: var(--foreground);
|
||||
background-color: var(--badge);
|
||||
}
|
||||
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.selected) {
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--foreground);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:not(.colored) {
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--foreground);
|
||||
background: transparent;
|
||||
|
||||
&.selected {
|
||||
color: var(--background);
|
||||
background: var(--foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
radar-app/src/components/Badge/Badge.tsx
Normal file
105
radar-app/src/components/Badge/Badge.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
CSSProperties,
|
||||
ComponentPropsWithoutRef,
|
||||
ReactNode,
|
||||
useMemo,
|
||||
} from "react";
|
||||
|
||||
import styles from "./Badge.module.css";
|
||||
|
||||
import { getFlag, getRing } from "@/lib/data";
|
||||
import { formatRelease } from "@/lib/format";
|
||||
import { Flag } from "@/lib/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface BadgeProps extends ComponentPropsWithoutRef<"span"> {
|
||||
children?: ReactNode;
|
||||
color?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
size?: "small" | "medium" | "large";
|
||||
}
|
||||
|
||||
export function Badge({
|
||||
children,
|
||||
color,
|
||||
size = "medium",
|
||||
selectable,
|
||||
selected,
|
||||
...props
|
||||
}: BadgeProps) {
|
||||
const style = useMemo(
|
||||
() => (color ? ({ "--badge": color } as CSSProperties) : undefined),
|
||||
[color],
|
||||
);
|
||||
|
||||
const Component = props.onClick ? "button" : "span";
|
||||
|
||||
return (
|
||||
<Component
|
||||
{...props}
|
||||
style={style}
|
||||
className={cn(
|
||||
props.className,
|
||||
styles.badge,
|
||||
styles[`size-${size}`],
|
||||
color && styles.colored,
|
||||
selectable && styles.selectable,
|
||||
selected && styles.selected,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
interface RingBadgeProps extends Omit<BadgeProps, "color" | "children"> {
|
||||
ring: string;
|
||||
release?: string;
|
||||
}
|
||||
|
||||
export function RingBadge({
|
||||
ring: ringName,
|
||||
release,
|
||||
...props
|
||||
}: RingBadgeProps) {
|
||||
const ring = getRing(ringName);
|
||||
if (!ring) return null;
|
||||
|
||||
const label = release
|
||||
? `${ring.title} | ${formatRelease(release)}`
|
||||
: ring.title;
|
||||
|
||||
return (
|
||||
<Badge color={ring.color} {...props}>
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
// Type guard to check if flag has the required attributes
|
||||
function hasRequiredFlagAttributes(flag: any): flag is {
|
||||
color: string;
|
||||
title: string;
|
||||
titleShort: string;
|
||||
} {
|
||||
return "color" in flag && "title" in flag && "titleShort" in flag;
|
||||
}
|
||||
|
||||
interface FlagBadgeProps
|
||||
extends Omit<BadgeProps, "color" | "children" | "size"> {
|
||||
flag: Flag;
|
||||
short?: boolean;
|
||||
}
|
||||
|
||||
export function FlagBadge({ flag: flagName, short, ...props }: FlagBadgeProps) {
|
||||
if (flagName === Flag.Default) return null;
|
||||
const flag = getFlag(flagName);
|
||||
if (!flag || !hasRequiredFlagAttributes(flag)) return null;
|
||||
|
||||
return (
|
||||
<Badge color={flag.color} size="small" {...props}>
|
||||
{short ? flag.titleShort : flag.title}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
8
radar-app/src/components/Filter/Filter.module.css
Normal file
8
radar-app/src/components/Filter/Filter.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.filter {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
25
radar-app/src/components/Filter/Filter.tsx
Normal file
25
radar-app/src/components/Filter/Filter.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import styles from "./Filter.module.css";
|
||||
|
||||
import { QueryFilter } from "@/components/Filter/QueryFilter";
|
||||
import { RingFilter } from "@/components/Filter/RingFilter";
|
||||
|
||||
interface FilterProps {
|
||||
query?: string;
|
||||
onQueryChange: (query: string) => void;
|
||||
ring?: string;
|
||||
onRingChange: (ring: string) => void;
|
||||
}
|
||||
|
||||
export function Filter({
|
||||
query,
|
||||
onQueryChange,
|
||||
ring,
|
||||
onRingChange,
|
||||
}: FilterProps) {
|
||||
return (
|
||||
<div className={styles.filter}>
|
||||
<QueryFilter value={query} onChange={onQueryChange} />
|
||||
<RingFilter value={ring} onChange={onRingChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
29
radar-app/src/components/Filter/QueryFilter.module.css
Normal file
29
radar-app/src/components/Filter/QueryFilter.module.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.filter {
|
||||
flex: 1 1 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
fill: var(--highlight);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.filter {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
39
radar-app/src/components/Filter/QueryFilter.tsx
Normal file
39
radar-app/src/components/Filter/QueryFilter.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ChangeEvent, useEffect, useState } from "react";
|
||||
|
||||
import Search from "../Icons/Search";
|
||||
import styles from "./QueryFilter.module.css";
|
||||
|
||||
import { getLabel } from "@/lib/data";
|
||||
|
||||
interface QueryFilterProps {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export function QueryFilter({ value, onChange }: QueryFilterProps) {
|
||||
const [val, setVal] = useState(value);
|
||||
const _onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setVal(e.target.value);
|
||||
onChange(e.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setVal(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className={styles.filter}>
|
||||
<input
|
||||
className={styles.input}
|
||||
id="search"
|
||||
type="search"
|
||||
placeholder={getLabel("searchPlaceholder")}
|
||||
value={val}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
<button className={styles.button} type="submit">
|
||||
<Search className={styles.icon} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
radar-app/src/components/Filter/RingFilter.module.css
Normal file
8
radar-app/src/components/Filter/RingFilter.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.filter {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
43
radar-app/src/components/Filter/RingFilter.tsx
Normal file
43
radar-app/src/components/Filter/RingFilter.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import styles from "./RingFilter.module.css";
|
||||
|
||||
import { Badge, RingBadge } from "@/components/Badge/Badge";
|
||||
import { getRings } from "@/lib/data";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface RingFilterProps {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function RingFilter({ value, onChange, className }: RingFilterProps) {
|
||||
const rings = getRings();
|
||||
|
||||
return (
|
||||
<ul className={cn(styles.filter, className)}>
|
||||
<li>
|
||||
<Badge
|
||||
size="large"
|
||||
selectable
|
||||
selected={!value}
|
||||
onClick={() => {
|
||||
onChange("");
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Badge>
|
||||
</li>
|
||||
{rings.map((ring) => (
|
||||
<li key={ring.id}>
|
||||
<RingBadge
|
||||
ring={ring.id}
|
||||
size="large"
|
||||
selectable
|
||||
selected={value === ring.id}
|
||||
onClick={() => onChange(ring.id)}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
54
radar-app/src/components/Footer/Footer.module.css
Normal file
54
radar-app/src/components/Footer/Footer.module.css
Normal file
@@ -0,0 +1,54 @@
|
||||
.branding {
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 20px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 12px;
|
||||
margin: 0 0 30px;
|
||||
}
|
||||
|
||||
.imprint {
|
||||
opacity: 0.7;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
text-decoration: underline;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.branding {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0 50px 0;
|
||||
}
|
||||
|
||||
.imprint {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1023px) {
|
||||
.socialLinks {
|
||||
flex-wrap: wrap;
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
20
radar-app/src/components/Footer/Footer.tsx
Normal file
20
radar-app/src/components/Footer/Footer.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import styles from "./Footer.module.css";
|
||||
|
||||
import { SocialLinks } from "@/components/SocialLinks/SocialLinks";
|
||||
import { getAppName, getImprintUrl, getLabel, getLogoUrl } from "@/lib/data";
|
||||
|
||||
export function Footer() {
|
||||
const logoUrl = getLogoUrl();
|
||||
return (
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.branding}>
|
||||
<img src={logoUrl} className={styles.logo} alt={getAppName()} />
|
||||
<p className={styles.description}>{getLabel("footer")}</p>
|
||||
<SocialLinks className={styles.socialLinks} />
|
||||
</div>
|
||||
<a href={getImprintUrl()} className={styles.imprint} target="_blank">
|
||||
{getLabel("imprint")}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
119
radar-app/src/components/ItemDetail/ItemDetail.module.css
Normal file
119
radar-app/src/components/ItemDetail/ItemDetail.module.css
Normal file
@@ -0,0 +1,119 @@
|
||||
.header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 30px 0 0;
|
||||
}
|
||||
|
||||
.editLink {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.revision {
|
||||
padding: 30px 0 15px 35px;
|
||||
margin-left: 20px;
|
||||
border-left: 1px solid var(--border);
|
||||
|
||||
&:hover {
|
||||
.editLink {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.release {
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
padding: 10px 0;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--background);
|
||||
float: left;
|
||||
margin: -15px 0 0 -60px;
|
||||
}
|
||||
|
||||
.notMaintainedIcon {
|
||||
fill: currentColor;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 8px auto;
|
||||
}
|
||||
|
||||
.ring {
|
||||
float: left;
|
||||
margin: -45px 0 0 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
background: var(--content);
|
||||
color: var(--text);
|
||||
border-radius: 6px;
|
||||
padding: 30px 15px;
|
||||
}
|
||||
|
||||
.content a {
|
||||
color: var(--link);
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.revision {
|
||||
padding: 30px 0 15px 50px;
|
||||
margin-left: 38px;
|
||||
}
|
||||
|
||||
.release {
|
||||
font-size: 18px;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
padding: 15px 0;
|
||||
margin: -15px 0 0 -90px;
|
||||
}
|
||||
|
||||
.ring {
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
/* special styles for revisions without content */
|
||||
.revision.noContent {
|
||||
.content {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.ring {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.revision.hint {
|
||||
.content {
|
||||
font-size: 14px;
|
||||
background: var(--border);
|
||||
color: var(--foreground);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user