afkaar-gen 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/SKILL.md +80 -0
- package/bin/interactive.js +10 -0
- package/interactive.html +575 -0
- package/package.json +42 -0
- package/references/architecture.md +185 -0
- package/references/figma-mode.md +57 -0
- package/references/inspiration-mode.md +71 -0
- package/references/integrations.md +111 -0
- package/references/iteration-mode.md +60 -0
- package/references/questions.md +198 -0
- package/references/security-checklist.md +218 -0
- package/references/seo-guide.md +183 -0
package/interactive.html
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>afkaar-gen — Questionnaire interactif</title>
|
|
7
|
+
<style>
|
|
8
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
|
+
body{font-family:'Segoe UI',system-ui,-apple-system,sans-serif;background:#f5f5f8;color:#14131c;min-height:100vh;display:flex;justify-content:center;padding:20px}
|
|
10
|
+
.container{max-width:720px;width:100%}
|
|
11
|
+
|
|
12
|
+
/* Header */
|
|
13
|
+
.header{text-align:center;padding:32px 0 24px}
|
|
14
|
+
.header h1{font-size:24px;font-weight:700;letter-spacing:-.3px}
|
|
15
|
+
.header p{color:#5a596a;font-size:14px;margin-top:4px}
|
|
16
|
+
|
|
17
|
+
/* Progress */
|
|
18
|
+
.progress-bar{background:#e3e3ec;border-radius:99px;height:6px;margin-bottom:32px;overflow:hidden}
|
|
19
|
+
.progress-fill{background:#3b2fd4;height:100%;border-radius:99px;transition:width .4s ease;width:0}
|
|
20
|
+
|
|
21
|
+
/* Steps tabs */
|
|
22
|
+
.steps{display:flex;gap:8px;margin-bottom:32px}
|
|
23
|
+
.step-tab{padding:10px 18px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;border:2px solid #e3e3ec;background:#fff;color:#5a596a;transition:all .2s;flex:1;text-align:center}
|
|
24
|
+
.step-tab.active{border-color:#3b2fd4;color:#3b2fd4;background:#f0eeff}
|
|
25
|
+
.step-tab.done{background:#3b2fd4;color:#fff;border-color:#3b2fd4}
|
|
26
|
+
|
|
27
|
+
/* Step content */
|
|
28
|
+
.step{display:none}
|
|
29
|
+
.step.active{display:block}
|
|
30
|
+
.step-title{font-size:16px;font-weight:600;margin-bottom:20px;color:#14131c}
|
|
31
|
+
|
|
32
|
+
/* Question card */
|
|
33
|
+
.q-card{background:#fff;border-radius:14px;padding:20px;margin-bottom:20px;box-shadow:0 1px 3px rgba(0,0,0,.06)}
|
|
34
|
+
.q-label{font-size:15px;font-weight:600;margin-bottom:12px;color:#14131c}
|
|
35
|
+
.q-desc{font-size:13px;color:#5a596a;margin-bottom:14px;line-height:1.5}
|
|
36
|
+
|
|
37
|
+
/* Option grid */
|
|
38
|
+
.opt-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
|
39
|
+
.opt-grid.single{grid-template-columns:1fr}
|
|
40
|
+
.opt-grid.col3{grid-template-columns:1fr 1fr 1fr}
|
|
41
|
+
|
|
42
|
+
.opt-card{cursor:pointer;padding:12px 14px;border-radius:10px;border:2px solid #e3e3ec;background:#fff;transition:all .2s;font-size:13px;line-height:1.4}
|
|
43
|
+
.opt-card:hover{border-color:#b8b6d0;transform:translateY(-1px)}
|
|
44
|
+
.opt-card.selected{border-color:#3b2fd4;background:#f0eeff;box-shadow:0 2px 8px rgba(59,47,212,.12)}
|
|
45
|
+
.opt-card .badge{display:inline-block;background:#e3e3ec;border-radius:4px;padding:1px 6px;font-size:11px;font-weight:600;color:#5a596a;margin-right:6px}
|
|
46
|
+
.opt-card.selected .badge{background:#3b2fd4;color:#fff}
|
|
47
|
+
.opt-card.default{border-style:dashed}
|
|
48
|
+
.opt-card.default::after{content:" ★";color:#f5c518;font-weight:700}
|
|
49
|
+
|
|
50
|
+
/* Checkboxes for multi-select */
|
|
51
|
+
.check-grid{display:flex;flex-wrap:wrap;gap:6px}
|
|
52
|
+
.check-item{cursor:pointer;padding:8px 14px;border-radius:8px;border:2px solid #e3e3ec;background:#fff;transition:all .2s;font-size:13px;user-select:none;display:flex;align-items:center;gap:6px}
|
|
53
|
+
.check-item:hover{border-color:#b8b6d0}
|
|
54
|
+
.check-item.checked{border-color:#3b2fd4;background:#f0eeff}
|
|
55
|
+
.check-item .box{width:18px;height:18px;border-radius:4px;border:2px solid #b8b6d0;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#fff;transition:all .15s;flex-shrink:0}
|
|
56
|
+
.check-item.checked .box{background:#3b2fd4;border-color:#3b2fd4}
|
|
57
|
+
|
|
58
|
+
/* Input text */
|
|
59
|
+
.q-input{width:100%;padding:10px 14px;border-radius:8px;border:2px solid #e3e3ec;font-size:14px;font-family:inherit;outline:none;transition:border .2s;margin-top:8px}
|
|
60
|
+
.q-input:focus{border-color:#3b2fd4}
|
|
61
|
+
|
|
62
|
+
/* Buttons */
|
|
63
|
+
.btn-row{display:flex;gap:12px;margin-top:8px}
|
|
64
|
+
.btn{padding:12px 28px;border-radius:10px;font-size:14px;font-weight:600;cursor:pointer;border:none;transition:all .2s;font-family:inherit}
|
|
65
|
+
.btn-primary{background:#3b2fd4;color:#fff}
|
|
66
|
+
.btn-primary:hover{background:#2c23a8;transform:translateY(-1px)}
|
|
67
|
+
.btn-primary:disabled{background:#b8b6d0;cursor:default;transform:none}
|
|
68
|
+
.btn-ghost{background:transparent;color:#5a596a;border:2px solid #e3e3ec}
|
|
69
|
+
.btn-ghost:hover{background:#f0eeff;border-color:#3b2fd4;color:#3b2fd4}
|
|
70
|
+
|
|
71
|
+
/* Summary */
|
|
72
|
+
.summary-section{display:none}
|
|
73
|
+
.summary-section.active{display:block}
|
|
74
|
+
.summary-card{background:#fff;border-radius:14px;padding:24px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}
|
|
75
|
+
.summary-card h3{font-size:14px;font-weight:600;color:#5a596a;text-transform:uppercase;letter-spacing:.5px;margin-bottom:12px}
|
|
76
|
+
.summary-item{padding:6px 0;font-size:14px;border-bottom:1px solid #f0f0f5}
|
|
77
|
+
.summary-item:last-child{border-bottom:none}
|
|
78
|
+
.summary-item .key{color:#5a596a;display:inline;margin-right:8px}
|
|
79
|
+
.summary-item .val{color:#14131c;font-weight:500}
|
|
80
|
+
.tag{display:inline-block;background:#f0eeff;color:#3b2fd4;border-radius:6px;padding:2px 10px;font-size:12px;font-weight:600;margin:2px}
|
|
81
|
+
.tag-off{background:#f0f0f5;color:#b8b6d0}
|
|
82
|
+
|
|
83
|
+
@media(max-width:600px){
|
|
84
|
+
.opt-grid{grid-template-columns:1fr!important}
|
|
85
|
+
.opt-grid.col3{grid-template-columns:1fr!important}
|
|
86
|
+
.steps{flex-wrap:wrap}
|
|
87
|
+
.step-tab{flex:none;width:calc(50% - 4px)}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Section picker */
|
|
91
|
+
.section-picker{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
|
92
|
+
.section-item{cursor:pointer;padding:8px 12px;border-radius:8px;border:2px solid #e3e3ec;background:#fff;transition:all .2s;font-size:13px;display:flex;align-items:center;gap:8px}
|
|
93
|
+
.section-item.selected{border-color:#3b2fd4;background:#f0eeff}
|
|
94
|
+
|
|
95
|
+
/* Service entry */
|
|
96
|
+
.service-list{margin-bottom:12px}
|
|
97
|
+
.service-entry{background:#f8f8fc;border-radius:8px;padding:10px 14px;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center;font-size:13px}
|
|
98
|
+
.service-entry .del{cursor:pointer;color:#c62838;font-weight:700;font-size:16px;padding:0 4px}
|
|
99
|
+
.add-svc-btn{display:inline-flex;align-items:center;gap:6px;cursor:pointer;color:#3b2fd4;font-weight:600;font-size:13px;padding:8px 12px;border-radius:8px;border:2px dashed #b8b6d0;background:transparent;margin-top:6px;transition:all .2s}
|
|
100
|
+
.add-svc-btn:hover{border-color:#3b2fd4;background:#f0eeff}
|
|
101
|
+
</style>
|
|
102
|
+
</head>
|
|
103
|
+
<body>
|
|
104
|
+
<div class="container">
|
|
105
|
+
<div class="header">
|
|
106
|
+
<h1>✏️ afkaar-gen</h1>
|
|
107
|
+
<p>Répondez aux questions pour générer votre site web</p>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
|
|
111
|
+
|
|
112
|
+
<div class="steps" id="stepTabs">
|
|
113
|
+
<div class="step-tab active" data-step="0">1. Identité</div>
|
|
114
|
+
<div class="step-tab" data-step="1">2. Contenu</div>
|
|
115
|
+
<div class="step-tab" data-step="2">3. Technique</div>
|
|
116
|
+
<div class="step-tab" data-step="3">4. Finalisation</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div id="step0" class="step active"></div>
|
|
120
|
+
<div id="step1" class="step"></div>
|
|
121
|
+
<div id="step2" class="step"></div>
|
|
122
|
+
<div id="step3" class="step"></div>
|
|
123
|
+
|
|
124
|
+
<div class="summary-section" id="summarySection">
|
|
125
|
+
<h2 style="font-size:20px;margin-bottom:20px">📋 Récapitulatif</h2>
|
|
126
|
+
<div id="summaryContent"></div>
|
|
127
|
+
<div class="btn-row" style="margin-top:20px">
|
|
128
|
+
<button class="btn btn-ghost" onclick="goToStep(0)">Modifier</button>
|
|
129
|
+
<button class="btn btn-primary" id="exportBtn" onclick="exportJSON()">Exporter le projet</button>
|
|
130
|
+
</div>
|
|
131
|
+
<p style="color:#5a596a;font-size:13px;margin-top:12px">L'export génère un fichier JSON à donner à l'agent AI pour générer le site.</p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<script>
|
|
136
|
+
// ===== DATA =====
|
|
137
|
+
const data = {
|
|
138
|
+
name: '', domain: '', logo: '', colors: '', style: '',
|
|
139
|
+
sections: [], services: [], extraPages: [], socials: [],
|
|
140
|
+
blog: '', admin: '', integrations: [], otherFeat: '',
|
|
141
|
+
inspiration: '', mockup: '', phone: '', email: '', address: '',
|
|
142
|
+
hosting: '', lang: ''
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
let currentStep = 0;
|
|
146
|
+
let serviceCount = 0;
|
|
147
|
+
|
|
148
|
+
// ===== QUESTIONS =====
|
|
149
|
+
const steps = [
|
|
150
|
+
// Step 0: Identité
|
|
151
|
+
[
|
|
152
|
+
{
|
|
153
|
+
id: 'name', label: "Nom de l'entreprise", desc: "Comment s'appelle votre entreprise ou projet ?",
|
|
154
|
+
type: 'choices', options: [
|
|
155
|
+
{ val: 'perso', label: 'Mon Nom / Prénom', def: false },
|
|
156
|
+
{ val: 'agence', label: 'Mon Agence Digitale', def: true },
|
|
157
|
+
{ val: 'marque', label: 'Ma Marque', def: false },
|
|
158
|
+
{ val: 'custom', label: 'Autre (à préciser)', def: false, input: true }
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'domain', label: 'Nom de domaine', desc: "Quel URL pour votre site ?",
|
|
163
|
+
type: 'choices', options: [
|
|
164
|
+
{ val: 'auto', label: 'mon-agence.tn', def: true },
|
|
165
|
+
{ val: 'later', label: 'Je ne sais pas encore', def: false },
|
|
166
|
+
{ val: 'custom', label: 'Autre (à préciser)', def: false, input: true }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'logo', label: 'Logo', desc: "Avez-vous un logo ?",
|
|
171
|
+
type: 'choices', options: [
|
|
172
|
+
{ val: 'upload', label: 'Je l\'envoie en image', def: false, input: true, inputPh: 'Collez l\'image en base64 ici (optionnel)' },
|
|
173
|
+
{ val: 'describe', label: 'Je le décris, vous le créez', def: false, input: true, inputPh: 'Décrivez votre logo' },
|
|
174
|
+
{ val: 'none', label: 'Pas de logo → placeholder', def: true }
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'colors', label: 'Palette de couleurs', desc: "Choisissez une palette ou donnez vos couleurs",
|
|
179
|
+
type: 'choices', options: [
|
|
180
|
+
{ val: 'blue-orange', label: '🟦🟧 Bleu marine + Orange', sub: 'Pro & dynamique', def: false },
|
|
181
|
+
{ val: 'green-beige', label: '🟩⬜ Vert sombre + Beige', sub: 'Nature & sérénité', def: false },
|
|
182
|
+
{ val: 'purple-pink', label: '🟣🩷 Violet + Rose', sub: 'Créatif & moderne', def: false },
|
|
183
|
+
{ val: 'black-gold', label: '⬛🟨 Noir + Blanc + Or', sub: 'Luxe & sobre', def: true },
|
|
184
|
+
{ val: 'red-grey', label: '🟥⬜ Rouge + Gris', sub: 'Puissant & technique', def: false },
|
|
185
|
+
{ val: 'blue-white', label: '🟦⬜ Bleu clair + Blanc', sub: 'Tech & épuré', def: false },
|
|
186
|
+
{ val: 'custom', label: 'Personnalisé', sub: 'Donnez vos couleurs', def: false, input: true, inputPh: 'Ex: #ff6600 et #1a237e' }
|
|
187
|
+
],
|
|
188
|
+
multi: true
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'style', label: 'Style visuel', desc: "Quelle ambiance pour le site ?",
|
|
192
|
+
type: 'choices', options: [
|
|
193
|
+
{ val: 'pro', label: '💼 Professionnel & sobre', sub: 'Lignes nettes, sérieux', def: true },
|
|
194
|
+
{ val: 'modern', label: '🚀 Moderne & dynamique', sub: 'Animations, audacieux', def: false },
|
|
195
|
+
{ val: 'creative', label: '🎨 Créatif & coloré', sub: 'Original, qui démarque', def: false },
|
|
196
|
+
{ val: 'minimal', label: '◻️ Minimaliste', sub: 'Épuré, beaucoup d\'espace', def: false }
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
// Step 1: Contenu
|
|
201
|
+
[
|
|
202
|
+
{
|
|
203
|
+
id: 'sections', label: 'Sections de la page d\'accueil', desc: "Choisissez les rubriques à afficher",
|
|
204
|
+
type: 'checkboxes', options: [
|
|
205
|
+
{ val: 'hero', label: 'Hero', def: true },
|
|
206
|
+
{ val: 'about', label: 'À propos', def: true },
|
|
207
|
+
{ val: 'services', label: 'Services', def: true },
|
|
208
|
+
{ val: 'portfolio', label: 'Réalisations', def: true },
|
|
209
|
+
{ val: 'team', label: 'Équipe', def: false },
|
|
210
|
+
{ val: 'testimonials', label: 'Témoignages', def: true },
|
|
211
|
+
{ val: 'stats', label: 'Chiffres clés', def: false },
|
|
212
|
+
{ val: 'faq', label: 'FAQ', def: false },
|
|
213
|
+
{ val: 'blog', label: 'Blog / Actu', def: false },
|
|
214
|
+
{ val: 'contact', label: 'Contact', def: true },
|
|
215
|
+
{ val: 'clients', label: 'Marque clients', def: true }
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'services', label: 'Vos services', desc: "Ajoutez vos services un par un",
|
|
220
|
+
type: 'services'
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 'extraPages', label: 'Pages supplémentaires', desc: "En plus de l'accueil",
|
|
224
|
+
type: 'checkboxes', options: [
|
|
225
|
+
{ val: 'legal', label: 'Mentions légales', def: true },
|
|
226
|
+
{ val: 'faq', label: 'FAQ dédiée', def: false },
|
|
227
|
+
{ val: 'recruit', label: 'Recrutement', def: false },
|
|
228
|
+
{ val: 'gallery', label: 'Galerie photos', def: false },
|
|
229
|
+
{ val: 'cgu', label: 'CGV / CGU', def: false }
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'socials', label: 'Réseaux sociaux', desc: "Sélectionnez vos réseaux",
|
|
234
|
+
type: 'checkboxes', options: [
|
|
235
|
+
{ val: 'facebook', label: 'Facebook', def: true },
|
|
236
|
+
{ val: 'instagram', label: 'Instagram', def: true },
|
|
237
|
+
{ val: 'linkedin', label: 'LinkedIn', def: true },
|
|
238
|
+
{ val: 'tiktok', label: 'TikTok', def: false },
|
|
239
|
+
{ val: 'youtube', label: 'YouTube', def: false },
|
|
240
|
+
{ val: 'whatsapp', label: 'WhatsApp', def: true }
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
// Step 2: Technique
|
|
245
|
+
[
|
|
246
|
+
{
|
|
247
|
+
id: 'blog', label: 'Blog', desc: "Voulez-vous un blog pour le référencement ?",
|
|
248
|
+
type: 'choices', options: [
|
|
249
|
+
{ val: 'admin', label: 'Oui, avec zone d\'admin (j\'écris)', def: true },
|
|
250
|
+
{ val: 'managed', label: 'Oui, mais vous gérez les articles', def: false },
|
|
251
|
+
{ val: 'none', label: 'Non, pas de blog', def: false }
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: 'admin', label: 'Administration', desc: "Voulez-vous pouvoir modifier le site vous-même ?",
|
|
256
|
+
type: 'choices', options: [
|
|
257
|
+
{ val: 'yes', label: 'Oui, admin complet (CRUD + dashboard)', def: true },
|
|
258
|
+
{ val: 'no', label: 'Non, vous gérez tout', def: false }
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'integrations', label: 'Intégrations', desc: "Connectez vos outils existants",
|
|
263
|
+
type: 'checkboxes', options: [
|
|
264
|
+
{ val: 'whatsapp', label: 'WhatsApp — notifications leads', def: true },
|
|
265
|
+
{ val: 'calendly', label: 'Calendly — prise de RDV', def: false },
|
|
266
|
+
{ val: 'gtm', label: 'Google Analytics — déjà inclus', def: true },
|
|
267
|
+
{ val: 'pixel', label: 'Facebook Pixel', def: false },
|
|
268
|
+
{ val: 'newsletter', label: 'Newsletter (Mailchimp/Brevo)', def: false },
|
|
269
|
+
{ val: 'payment', label: 'Paiement (Stripe/Paymee)', def: false },
|
|
270
|
+
{ val: 'slack', label: 'Slack/Discord — notifications', def: false }
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: 'otherFeat', label: 'Autres fonctionnalités', desc: "Quelque chose d'autre ?",
|
|
275
|
+
type: 'choices', options: [
|
|
276
|
+
{ val: 'devis', label: 'Calculateur de devis', def: false },
|
|
277
|
+
{ val: 'chat', label: 'Chat en direct', def: false },
|
|
278
|
+
{ val: 'client-area', label: 'Espace client privé', def: false },
|
|
279
|
+
{ val: 'booking', label: 'Système de réservation', def: false },
|
|
280
|
+
{ val: 'forum', label: 'Forum / communauté', def: false },
|
|
281
|
+
{ val: 'nothing', label: 'Rien de plus', def: true },
|
|
282
|
+
{ val: 'custom', label: 'Autre (à préciser)', def: false, input: true, inputPh: 'Décrivez' }
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'inspiration', label: 'Site d\'inspiration', desc: "Un site que vous aimez ?",
|
|
287
|
+
type: 'choices', options: [
|
|
288
|
+
{ val: 'url', label: 'Oui, je donne une URL', def: false, input: true, inputPh: 'https://...' },
|
|
289
|
+
{ val: 'image', label: 'Oui, j\'ai une capture / maquette', def: false },
|
|
290
|
+
{ val: 'none', label: 'Non, on part de zéro', def: true }
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
],
|
|
294
|
+
// Step 3: Finalisation
|
|
295
|
+
[
|
|
296
|
+
{
|
|
297
|
+
id: 'mockup', label: 'Maquette / Design', desc: "Avez-vous une maquette Figma ou une image ?",
|
|
298
|
+
type: 'choices', options: [
|
|
299
|
+
{ val: 'figma', label: 'Lien Figma', def: false, input: true, inputPh: 'https://www.figma.com/file/...' },
|
|
300
|
+
{ val: 'image', label: 'Capture d\'écran (base64)', def: false, input: true, inputPh: 'Collez l\'image ici' },
|
|
301
|
+
{ val: 'none', label: 'Pas de maquette', def: true }
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: 'phone', label: 'Téléphone', desc: "Numéro de contact",
|
|
306
|
+
type: 'input', placeholder: '+216 XX XXX XXX', def: '+216 53 467 943'
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: 'email', label: 'Email', desc: "Adresse email de contact",
|
|
310
|
+
type: 'input', placeholder: 'contact@monagence.tn', def: 'contact@monagence.tn'
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'address', label: 'Adresse', desc: "Adresse physique (optionnelle)",
|
|
314
|
+
type: 'input', placeholder: 'Ville, Rue...', def: ''
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: 'hosting', label: 'Hébergement', desc: "Où sera hébergé le site ?",
|
|
318
|
+
type: 'choices', options: [
|
|
319
|
+
{ val: 'unknown', label: 'Je ne sais pas encore', def: true },
|
|
320
|
+
{ val: 'shared', label: 'Hébergement mutualisé', def: false },
|
|
321
|
+
{ val: 'vps', label: 'VPS / serveur dédié', def: false },
|
|
322
|
+
{ val: 'other', label: 'Autre', def: false, input: true, inputPh: 'Précisez' }
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'lang', label: 'Langue du site', desc: "Dans quelle(s) langue(s) ?",
|
|
327
|
+
type: 'choices', options: [
|
|
328
|
+
{ val: 'fr', label: 'Français', def: true },
|
|
329
|
+
{ val: 'en', label: 'Anglais', def: false },
|
|
330
|
+
{ val: 'ar', label: 'Arabe', def: false },
|
|
331
|
+
{ val: 'fr-en', label: 'Français + Anglais', def: false },
|
|
332
|
+
{ val: 'fr-ar', label: 'Français + Arabe', def: false }
|
|
333
|
+
]
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
// ===== RENDER =====
|
|
339
|
+
function renderStep(idx) {
|
|
340
|
+
const el = document.getElementById('step' + idx);
|
|
341
|
+
let html = '<div class="step-title">' + getStepTitle(idx) + '</div>';
|
|
342
|
+
steps[idx].forEach(q => {
|
|
343
|
+
html += '<div class="q-card">';
|
|
344
|
+
html += '<div class="q-label">' + q.label + '</div>';
|
|
345
|
+
if (q.desc) html += '<div class="q-desc">' + q.desc + '</div>';
|
|
346
|
+
html += renderQuestion(q);
|
|
347
|
+
html += '</div>';
|
|
348
|
+
});
|
|
349
|
+
html += '<div class="btn-row">';
|
|
350
|
+
if (idx > 0) html += '<button class="btn btn-ghost" onclick="goToStep(' + (idx - 1) + ')">← Précédent</button>';
|
|
351
|
+
html += '<button class="btn btn-primary" id="nextBtn' + idx + '" onclick="nextStep(' + idx + ')">' + (idx < 3 ? 'Suivant →' : 'Voir le résumé') + '</button>';
|
|
352
|
+
html += '</div>';
|
|
353
|
+
el.innerHTML = html;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function getStepTitle(idx) {
|
|
357
|
+
const titles = ['Étape 1 — Identité', 'Étape 2 — Contenu', 'Étape 3 — Technique', 'Étape 4 — Finalisation'];
|
|
358
|
+
return titles[idx];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function renderQuestion(q) {
|
|
362
|
+
if (q.type === 'choices') return renderChoices(q);
|
|
363
|
+
if (q.type === 'checkboxes') return renderCheckboxes(q);
|
|
364
|
+
if (q.type === 'services') return renderServices(q);
|
|
365
|
+
if (q.type === 'input') return renderInput(q);
|
|
366
|
+
return '';
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function renderChoices(q) {
|
|
370
|
+
const isColor = q.id === 'colors';
|
|
371
|
+
const cols = isColor ? 'col3' : 'single';
|
|
372
|
+
const val = Array.isArray(data[q.id]) ? data[q.id] : [data[q.id]];
|
|
373
|
+
let html = '<div class="opt-grid ' + cols + '">';
|
|
374
|
+
q.options.forEach((o, i) => {
|
|
375
|
+
const selected = val.includes(o.val);
|
|
376
|
+
const letter = String.fromCharCode(65 + i);
|
|
377
|
+
html += '<div class="opt-card' + (selected ? ' selected' : '') + (o.def ? ' default' : '') + '" onclick="selectChoice(\'' + q.id + '\',\'' + o.val + '\',' + (q.multi ? 'true' : 'false') + ')">';
|
|
378
|
+
html += '<span class="badge">' + letter + '</span>';
|
|
379
|
+
if (o.sub) html += '<span style="display:block;font-size:12px;color:#5a596a;margin-top:2px">' + o.sub + '</span>';
|
|
380
|
+
html += o.label;
|
|
381
|
+
html += '</div>';
|
|
382
|
+
});
|
|
383
|
+
html += '</div>';
|
|
384
|
+
// Input for custom
|
|
385
|
+
q.options.forEach(o => {
|
|
386
|
+
if (o.input) {
|
|
387
|
+
const show = Array.isArray(data[q.id]) ? data[q.id].includes(o.val) : data[q.id] === o.val;
|
|
388
|
+
html += '<input class="q-input" id="input_' + q.id + '_' + o.val + '" placeholder="' + (o.inputPh || '') + '" style="' + (show ? '' : 'display:none') + '" oninput="updateCustom(\'' + q.id + '\',\'' + o.val + '\')">';
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
return html;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function renderCheckboxes(q) {
|
|
395
|
+
const val = data[q.id] || [];
|
|
396
|
+
let html = '<div class="check-grid">';
|
|
397
|
+
q.options.forEach(o => {
|
|
398
|
+
const checked = val.includes(o.val);
|
|
399
|
+
html += '<div class="check-item' + (checked ? ' checked' : '') + (o.def ? ' default' : '') + '" onclick="toggleCheck(\'' + q.id + '\',\'' + o.val + '\')">';
|
|
400
|
+
html += '<div class="box">' + (checked ? '✓' : '') + '</div>';
|
|
401
|
+
html += o.label;
|
|
402
|
+
html += '</div>';
|
|
403
|
+
});
|
|
404
|
+
html += '</div>';
|
|
405
|
+
return html;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function renderServices(q) {
|
|
409
|
+
let html = '<div class="service-list" id="svcList">';
|
|
410
|
+
(data.services || []).forEach((s, i) => {
|
|
411
|
+
html += '<div class="service-entry"><span>' + s.name + '</span><span class="del" onclick="removeService(' + i + ')">✕</span></div>';
|
|
412
|
+
});
|
|
413
|
+
html += '</div>';
|
|
414
|
+
html += '<div class="add-svc-btn" onclick="addService()">+ Ajouter un service</div>';
|
|
415
|
+
return html;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function renderInput(q) {
|
|
419
|
+
return '<input class="q-input" id="input_' + q.id + '" placeholder="' + (q.placeholder || '') + '" value="' + (data[q.id] || '') + '" oninput="data[\'' + q.id + '\']=this.value">';
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ===== INTERACTIONS =====
|
|
423
|
+
function selectChoice(id, val, multi) {
|
|
424
|
+
if (multi) {
|
|
425
|
+
if (!data[id]) data[id] = [];
|
|
426
|
+
const arr = data[id];
|
|
427
|
+
const idx = arr.indexOf(val);
|
|
428
|
+
if (idx >= 0) arr.splice(idx, 1); else arr.push(val);
|
|
429
|
+
// Show/hide associated inputs
|
|
430
|
+
renderStep(currentStep);
|
|
431
|
+
} else {
|
|
432
|
+
data[id] = val;
|
|
433
|
+
renderStep(currentStep);
|
|
434
|
+
}
|
|
435
|
+
updateProgress();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function toggleCheck(id, val) {
|
|
439
|
+
if (!data[id]) data[id] = [];
|
|
440
|
+
const arr = data[id];
|
|
441
|
+
const idx = arr.indexOf(val);
|
|
442
|
+
if (idx >= 0) arr.splice(idx, 1); else arr.push(val);
|
|
443
|
+
renderStep(currentStep);
|
|
444
|
+
updateProgress();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function updateCustom(id, val) {
|
|
448
|
+
// custom input is stored in the input field
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function addService() {
|
|
452
|
+
const name = prompt('Nom du service :');
|
|
453
|
+
if (!name || !name.trim()) return;
|
|
454
|
+
const pts = prompt('3 points clés (séparés par des virgules) :');
|
|
455
|
+
if (!data.services) data.services = [];
|
|
456
|
+
data.services.push({ name: name.trim(), points: pts ? pts.split(',').map(s => s.trim()) : [] });
|
|
457
|
+
renderStep(currentStep);
|
|
458
|
+
updateProgress();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function removeService(idx) {
|
|
462
|
+
data.services.splice(idx, 1);
|
|
463
|
+
renderStep(currentStep);
|
|
464
|
+
updateProgress();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ===== NAVIGATION =====
|
|
468
|
+
function nextStep(idx) {
|
|
469
|
+
if (idx < 3) {
|
|
470
|
+
goToStep(idx + 1);
|
|
471
|
+
} else {
|
|
472
|
+
showSummary();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function goToStep(idx) {
|
|
477
|
+
currentStep = idx;
|
|
478
|
+
document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));
|
|
479
|
+
document.getElementById('step' + idx).classList.add('active');
|
|
480
|
+
document.querySelectorAll('.step-tab').forEach(t => t.classList.remove('active'));
|
|
481
|
+
document.querySelectorAll('.step-tab')[idx].classList.add('active');
|
|
482
|
+
renderStep(idx);
|
|
483
|
+
updateProgress();
|
|
484
|
+
document.getElementById('summarySection').classList.remove('active');
|
|
485
|
+
document.getElementById('summarySection').style.display = 'none';
|
|
486
|
+
document.getElementById('step' + idx).style.display = '';
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function updateProgress() {
|
|
490
|
+
let answered = 0;
|
|
491
|
+
let total = 0;
|
|
492
|
+
steps.forEach(step => step.forEach(q => {
|
|
493
|
+
total++;
|
|
494
|
+
if (q.type === 'choices') {
|
|
495
|
+
if (Array.isArray(data[q.id]) && data[q.id].length) answered++;
|
|
496
|
+
else if (typeof data[q.id] === 'string' && data[q.id]) answered++;
|
|
497
|
+
} else if (q.type === 'checkboxes') {
|
|
498
|
+
if (data[q.id] && data[q.id].length) answered++;
|
|
499
|
+
} else if (q.type === 'services') {
|
|
500
|
+
if (data.services && data.services.length) answered++;
|
|
501
|
+
} else if (q.type === 'input') {
|
|
502
|
+
if (data[q.id]) answered++;
|
|
503
|
+
}
|
|
504
|
+
}));
|
|
505
|
+
const pct = Math.round((answered / total) * 100);
|
|
506
|
+
document.getElementById('progressFill').style.width = pct + '%';
|
|
507
|
+
document.querySelectorAll('.step-tab').forEach((t, i) => {
|
|
508
|
+
t.classList.remove('done');
|
|
509
|
+
if (i < currentStep) t.classList.add('done');
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ===== SUMMARY =====
|
|
514
|
+
function showSummary() {
|
|
515
|
+
document.querySelectorAll('.step').forEach(s => { s.style.display = 'none'; s.classList.remove('active'); });
|
|
516
|
+
document.getElementById('summarySection').style.display = 'block';
|
|
517
|
+
document.getElementById('summarySection').classList.add('active');
|
|
518
|
+
|
|
519
|
+
const s = data;
|
|
520
|
+
let html = '';
|
|
521
|
+
html += summaryBlock('Identité', [
|
|
522
|
+
['Entreprise', s.name || '—'],
|
|
523
|
+
['Domaine', s.domain === 'auto' ? 'mon-agence.tn' : s.domain === 'later' ? 'À définir' : s.domain || '—'],
|
|
524
|
+
['Logo', s.logo === 'none' ? 'Placeholder' : s.logo === 'upload' ? 'Image fournie' : s.logo === 'describe' ? 'À créer' : '—'],
|
|
525
|
+
['Couleurs', Array.isArray(s.colors) ? s.colors.join(', ') : s.colors || '—'],
|
|
526
|
+
['Style', s.style || '—']
|
|
527
|
+
]);
|
|
528
|
+
html += summaryBlock('Contenu', [
|
|
529
|
+
['Sections', (s.sections || []).join(', ')],
|
|
530
|
+
['Services', (s.services || []).map(x => x.name).join(', ') || '—'],
|
|
531
|
+
['Pages sup.', (s.extraPages || []).join(', ').replace(/legal/g,'Mentions légales').replace(/faq/g,'FAQ').replace(/recruit/g,'Recrutement').replace(/gallery/g,'Galerie').replace(/cgu/g,'CGV') || 'Aucune'],
|
|
532
|
+
['Réseaux', (s.socials || []).join(', ') || '—']
|
|
533
|
+
]);
|
|
534
|
+
html += summaryBlock('Technique', [
|
|
535
|
+
['Blog', s.blog === 'admin' ? 'Oui (admin)' : s.blog === 'managed' ? 'Oui (géré)' : 'Non'],
|
|
536
|
+
['Admin', s.admin === 'yes' ? 'Oui' : 'Non'],
|
|
537
|
+
['Intégrations', (s.integrations || []).join(', ') || '—'],
|
|
538
|
+
['Autres', s.otherFeat || '—'],
|
|
539
|
+
['Inspiration', s.inspiration || 'Aucune']
|
|
540
|
+
]);
|
|
541
|
+
html += summaryBlock('Finalisation', [
|
|
542
|
+
['Téléphone', s.phone || '—'],
|
|
543
|
+
['Email', s.email || '—'],
|
|
544
|
+
['Adresse', s.address || '—'],
|
|
545
|
+
['Hébergement', s.hosting || '—'],
|
|
546
|
+
['Langue', s.lang || '—']
|
|
547
|
+
]);
|
|
548
|
+
|
|
549
|
+
document.getElementById('summaryContent').innerHTML = html;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function summaryBlock(title, items) {
|
|
553
|
+
let html = '<div class="summary-card"><h3>' + title + '</h3>';
|
|
554
|
+
items.forEach(item => {
|
|
555
|
+
html += '<div class="summary-item"><span class="key">' + item[0] + ' :</span> <span class="val">' + item[1] + '</span></div>';
|
|
556
|
+
});
|
|
557
|
+
html += '</div>';
|
|
558
|
+
return html;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ===== EXPORT =====
|
|
562
|
+
function exportJSON() {
|
|
563
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
564
|
+
const a = document.createElement('a');
|
|
565
|
+
a.href = URL.createObjectURL(blob);
|
|
566
|
+
a.download = 'afkaar-gen-project.json';
|
|
567
|
+
a.click();
|
|
568
|
+
URL.revokeObjectURL(a.href);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ===== INIT =====
|
|
572
|
+
goToStep(0);
|
|
573
|
+
</script>
|
|
574
|
+
</body>
|
|
575
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "afkaar-gen",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI agent skill for generating PHP flat-file websites (type agence digitale). Works with Claude, opencode, and other AI coding agents.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"opencode",
|
|
7
|
+
"skill",
|
|
8
|
+
"claude",
|
|
9
|
+
"php",
|
|
10
|
+
"flat-file",
|
|
11
|
+
"website-generator",
|
|
12
|
+
"agence-digitale",
|
|
13
|
+
"afkars"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/iyed/afkaar_gen#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/iyed/afkaar_gen/issues"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Iyed <iyed@afkars.digital>",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/iyed/afkaar_gen.git"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"SKILL.md",
|
|
27
|
+
"references/",
|
|
28
|
+
"interactive.html",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"bin": {
|
|
32
|
+
"afkaar-gen": "bin/interactive.js"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"opencode": {
|
|
38
|
+
"skill": true,
|
|
39
|
+
"name": "afkaar-gen",
|
|
40
|
+
"description": "Génère un site vitrine PHP flat-file (type agence digitale)"
|
|
41
|
+
}
|
|
42
|
+
}
|