one-for-all-framework 1.0.0 → 3.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.
data/app/views/layout.erb CHANGED
@@ -9,7 +9,7 @@
9
9
  <!-- Fonts & Icons -->
10
10
  <link rel="preconnect" href="https://fonts.googleapis.com">
11
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Fira+Code&display=swap" rel="stylesheet">
12
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Fira+Code&family=VT323&display=swap" rel="stylesheet">
13
13
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
14
14
 
15
15
  <!-- Tailwind CSS CDN -->
@@ -27,13 +27,13 @@
27
27
  theme: {
28
28
  extend: {
29
29
  colors: {
30
- primary: '<%= FEATURES_CONFIG["theme"] == "light_glass" ? "#4f46e5" : "#818cf8" %>',
31
- secondary: '<%= FEATURES_CONFIG["theme"] == "light_glass" ? "#8b5cf6" : "#c084fc" %>',
32
- surface: '<%= FEATURES_CONFIG["theme"] == "light_glass" ? "rgba(255, 255, 255, 0.7)" : "rgba(17, 24, 39, 0.6)" %>',
33
- bg: '<%= FEATURES_CONFIG["theme"] == "light_glass" ? "#f8fafc" : "#030712" %>',
30
+ primary: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#4f46e5" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "#818cf8")) %>',
31
+ secondary: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#8b5cf6" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#0ff" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#040" : "#c084fc")) %>',
32
+ surface: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(255, 255, 255, 0.7)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "rgba(20, 20, 20, 0.8)" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#050505" : "rgba(17, 24, 39, 0.6)")) %>',
33
+ bg: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#f8fafc" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#000" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0a0a0a" : "#030712")) %>',
34
34
  },
35
35
  fontFamily: {
36
- sans: ['Outfit', 'sans-serif'],
36
+ sans: ['<%= FEATURES_CONFIG["theme"] == "retro_terminal" ? "VT323" : "Outfit" %>', 'sans-serif'],
37
37
  mono: ['Fira Code', 'monospace'],
38
38
  },
39
39
  backdropBlur: {
@@ -46,15 +46,29 @@
46
46
 
47
47
  <style>
48
48
  :root {
49
- --primary: <%= FEATURES_CONFIG["theme"] == "light_glass" ? "#4f46e5" : "#818cf8" %>;
50
- --secondary: <%= FEATURES_CONFIG["theme"] == "light_glass" ? "#8b5cf6" : "#c084fc" %>;
51
- --surface: <%= FEATURES_CONFIG["theme"] == "light_glass" ? "rgba(255, 255, 255, 0.7)" : "rgba(17, 24, 39, 0.6)" %>;
52
- --bg-color: <%= FEATURES_CONFIG["theme"] == "light_glass" ? "#f8fafc" : "#030712" %>;
53
- --glass-border: <%= FEATURES_CONFIG["theme"] == "light_glass" ? "rgba(0, 0, 0, 0.05)" : "rgba(255, 255, 255, 0.1)" %>;
49
+ --primary: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#4f46e5" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "#818cf8")) %>;
50
+ --secondary: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#8b5cf6" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#0ff" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#040" : "#c084fc")) %>;
51
+ --surface: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(255, 255, 255, 0.7)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "rgba(20, 20, 20, 0.8)" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#050505" : "rgba(17, 24, 39, 0.6)")) %>;
52
+ --bg-color: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#f8fafc" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#000" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0a0a0a" : "#030712")) %>;
53
+ --glass-border: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(0, 0, 0, 0.05)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "rgba(255, 255, 255, 0.1)")) %>;
54
+ --neo-shadow: 8px 8px 0px 0px var(--primary);
54
55
  }
55
56
 
56
57
  body {
57
58
  background-color: var(--bg-color);
59
+ <% if FEATURES_CONFIG['theme'] == 'cyber_sidebar' %>
60
+ background-image:
61
+ linear-gradient(rgba(255, 0, 255, 0.05) 1px, transparent 1px),
62
+ linear-gradient(90deg, rgba(255, 0, 255, 0.05) 1px, transparent 1px);
63
+ background-size: 50px 50px;
64
+ <% elsif FEATURES_CONFIG['theme'] == 'light_sidebar' %>
65
+ background-image:
66
+ radial-gradient(at 0% 0%, hsla(220,100%,95%,1) 0, transparent 50%),
67
+ radial-gradient(at 100% 0%, hsla(320,100%,95%,0.8) 0, transparent 50%);
68
+ <% elsif FEATURES_CONFIG['theme'] == 'retro_terminal' %>
69
+ background-image: radial-gradient(circle, rgba(0, 255, 0, 0.05) 1px, transparent 1px);
70
+ background-size: 20px 20px;
71
+ <% else %>
58
72
  background-image:
59
73
  <% if FEATURES_CONFIG['theme'] == 'light_glass' %>
60
74
  radial-gradient(at 0% 0%, hsla(220,100%,95%,1) 0, transparent 50%),
@@ -65,7 +79,25 @@
65
79
  radial-gradient(at 50% 0%, hsla(225,39%,30%,0.15) 0, transparent 50%),
66
80
  radial-gradient(at 100% 0%, hsla(339,49%,30%,0.15) 0, transparent 50%);
67
81
  <% end %>
82
+ <% end %>
68
83
  background-attachment: fixed;
84
+ <% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
85
+ overflow-x: hidden;
86
+ <% end %>
87
+ }
88
+
89
+ .neo-card {
90
+ background: var(--surface);
91
+ border: 3px solid var(--primary);
92
+ box-shadow: var(--neo-shadow);
93
+ border-radius: 0px;
94
+ padding: 2rem;
95
+ transition: all 0.2s;
96
+ }
97
+
98
+ .neo-card:hover {
99
+ transform: translate(-4px, -4px);
100
+ box-shadow: 12px 12px 0px 0px var(--secondary);
69
101
  }
70
102
 
71
103
  .glass-panel {
@@ -184,9 +216,95 @@
184
216
  .markdown-content h1 { font-size: 2.25rem; font-weight: 800; margin: 2rem 0 1rem; color: var(--primary); }
185
217
  .markdown-content p { font-size: 1.125rem; color: #475569; margin-bottom: 1.5rem; }
186
218
  .dark .markdown-content p { color: #cbd5e1; }
219
+
220
+ /* Cyber Theme Specifics */
221
+ .cyber-border {
222
+ border: 2px solid var(--primary);
223
+ box-shadow: 4px 4px 0px var(--secondary);
224
+ }
225
+
226
+ .cyber-btn {
227
+ background: var(--primary);
228
+ color: black;
229
+ font-weight: 900;
230
+ text-transform: uppercase;
231
+ padding: 1rem 2rem;
232
+ border: none;
233
+ box-shadow: 6px 6px 0px var(--secondary);
234
+ transition: all 0.1s;
235
+ }
236
+
237
+ .cyber-btn:active {
238
+ transform: translate(2px, 2px);
239
+ box-shadow: 4px 4px 0px var(--secondary);
240
+ }
241
+
242
+ /* Global Text Color for Dark Themes */
243
+ .dark {
244
+ color: #e2e8f0;
245
+ }
246
+
247
+ .dark h1, .dark h2, .dark h3, .dark h4, .dark h5, .dark h6 {
248
+ color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0f0' : '#ffffff' %> !important;
249
+ <% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
250
+ text-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
251
+ text-transform: uppercase;
252
+ letter-spacing: 2px;
253
+ <% end %>
254
+ }
255
+
256
+ .dark p, .dark span, .dark li, .dark a {
257
+ color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0f0' : 'rgba(255, 255, 255, 0.9)' %>;
258
+ <% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
259
+ font-family: 'VT323', monospace;
260
+ font-size: 1.25rem;
261
+ <% end %>
262
+ }
263
+
264
+ .dark .text-slate-500, .dark .text-slate-600, .dark .text-slate-700 {
265
+ color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0a0' : 'rgba(255, 255, 255, 0.7)' %> !important;
266
+ }
267
+
268
+ /* Scanlines for Retro Theme */
269
+ <% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
270
+ body::before {
271
+ content: " ";
272
+ display: block;
273
+ position: fixed;
274
+ top: 0; left: 0; bottom: 0; right: 0;
275
+ background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
276
+ linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
277
+ z-index: 999;
278
+ background-size: 100% 2px, 3px 100%;
279
+ pointer-events: none;
280
+ }
281
+
282
+ @keyframes flicker {
283
+ 0% { opacity: 0.97; }
284
+ 5% { opacity: 0.95; }
285
+ 10% { opacity: 0.9; }
286
+ 15% { opacity: 0.95; }
287
+ 20% { opacity: 0.98; }
288
+ 100% { opacity: 1; }
289
+ }
290
+
291
+ .dark main {
292
+ animation: flicker 0.15s infinite;
293
+ }
294
+
295
+ /* Fix visibility for buttons with background in Retro theme */
296
+ .dark .btn-premium,
297
+ .dark .bg-primary,
298
+ .dark .bg-white,
299
+ .dark .bg-primary > *,
300
+ .dark .bg-white > * {
301
+ color: #000000 !important;
302
+ text-shadow: none !important;
303
+ }
304
+ <% end %>
187
305
  </style>
188
306
  </head>
189
- <body class="<%= FEATURES_CONFIG['theme'] == 'dark_glass' ? 'dark' : '' %>">
307
+ <body class="<%= ['dark_glass', 'cyber_sidebar', 'retro_terminal'].include?(FEATURES_CONFIG['theme']) ? 'dark' : '' %>">
190
308
 
191
309
  <!-- Animated Background Shapes -->
192
310
  <div class="fixed inset-0 -z-10 overflow-hidden opacity-30 pointer-events-none">
@@ -194,7 +312,76 @@
194
312
  <div class="absolute top-1/2 -right-24 w-80 h-80 bg-secondary rounded-full blur-[100px] delay-700 animate-bounce"></div>
195
313
  </div>
196
314
 
197
- <!-- Navigation -->
315
+ <% if ['cyber_sidebar', 'light_sidebar'].include?(FEATURES_CONFIG['theme']) %>
316
+ <!-- Sidebar Layout -->
317
+ <div class="flex min-h-screen">
318
+ <!-- Sidebar -->
319
+ <aside class="fixed left-0 top-0 bottom-0 w-64 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'glass-panel !rounded-none border-r border-black/5' : 'bg-black border-r-4 border-primary shadow-[10px_0_30px_rgba(0,0,0,0.5)]' %> p-6 pb-12 z-50 hidden md:flex flex-col gap-10 overflow-y-auto custom-scrollbar">
320
+ <div class="flex items-center gap-3">
321
+ <% if FEATURES_CONFIG['theme'] == 'light_sidebar' %>
322
+ <div class="w-10 h-10 bg-primary text-white flex items-center justify-center font-black text-xl rounded-xl shadow-lg shadow-primary/20">OF</div>
323
+ <span class="text-2xl font-black tracking-tighter text-slate-800">SYSTEM</span>
324
+ <% else %>
325
+ <div class="w-10 h-10 bg-primary flex items-center justify-center text-white font-black text-xl">OF</div>
326
+ <span class="text-2xl font-black tracking-tighter text-white">SYSTEM</span>
327
+ <% end %>
328
+ </div>
329
+
330
+ <nav class="flex flex-col gap-2">
331
+ <a href="/" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path == '/' ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path == '/' ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
332
+ <i class="fas fa-grid-2 mr-3"></i> OVERVIEW
333
+ </a>
334
+ <% if FEATURES_CONFIG['type'] == 'e_commerce' %>
335
+ <a href="/cart" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path == '/cart' ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path == '/cart' ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
336
+ <i class="fas fa-shopping-cart mr-3"></i> MY CART
337
+ </a>
338
+ <% end %>
339
+ <% Page.where(is_nav: true, is_active: true).all.each do |p| %>
340
+ <a href="/<%= p.slug %>" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path == "/#{p.slug}" ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path == "/#{p.slug}" ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
341
+ <i class="fas fa-file-code mr-3"></i> <%= p.title.upcase %>
342
+ </a>
343
+ <% end %>
344
+ <a href="/dashboard" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path.start_with?('/dashboard') ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path.start_with?('/dashboard') ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
345
+ <i class="fas fa-terminal mr-3"></i> CONSOLE
346
+ </a>
347
+ </nav>
348
+
349
+ <div class="mt-auto space-y-4">
350
+ <div class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'bg-slate-100/50 border border-slate-200 text-slate-500' : 'bg-white/5 border border-white/10 text-slate-400' %> text-[10px] font-mono rounded-xl">
351
+ <% if FEATURES_CONFIG['theme'] == 'light_sidebar' %>
352
+ SYSTEM: <span class="text-indigo-600 font-bold">OPERATIONAL</span><br>
353
+ LICENSE: <span class="text-slate-800">ENTERPRISE</span>
354
+ <% else %>
355
+ PROTOCOL: <span class="text-green-500 animate-pulse">SECURED</span><br>
356
+ UPLINK: <span class="text-primary">ENCRYPTED</span>
357
+ <% end %>
358
+ </div>
359
+ <a href="https://github.com/IshikawaUta/one-for-all-framework" class="flex items-center justify-center w-full p-4 mb-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'bg-slate-900 text-white rounded-xl shadow-lg shadow-slate-200' : 'bg-primary text-black border-2 border-primary rounded-2xl shadow-[4px_4px_0px_var(--secondary)]' %> font-black hover:opacity-90 hover:-translate-y-0.5 transition-all">
360
+ <i class="fab fa-github mr-2 text-lg"></i> <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'VIEW SOURCE' : 'ACCESS REPO' %>
361
+ </a>
362
+ </div>
363
+ </aside>
364
+
365
+ <!-- Mobile Header for Sidebar Themes -->
366
+ <header class="md:hidden fixed top-0 left-0 right-0 h-16 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'glass-panel !rounded-none border-b border-black/5' : 'bg-black border-b-4 border-primary' %> z-[60] flex items-center justify-between px-6">
367
+ <span class="font-black <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'text-slate-800' : 'text-white' %>">OFA<span class="text-primary">.SYS</span></span>
368
+ <button id="mobile-menu-btn" class="text-primary"><i class="fas fa-bars text-2xl"></i></button>
369
+ </header>
370
+
371
+ <!-- Main Content Area -->
372
+ <main class="flex-1 md:ml-64 p-6 md:p-12 pt-24 md:pt-12">
373
+ <div class="max-w-5xl mx-auto">
374
+ <%= @content %>
375
+ </div>
376
+
377
+ <!-- Footer -->
378
+ <footer class="w-full py-12 mt-20 border-t <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'border-black/5' : 'border-white/5' %> text-center text-slate-500 text-sm">
379
+ <p>&copy; <%= Time.now.year %> One-For-All Framework.</p>
380
+ </footer>
381
+ </main>
382
+ </div>
383
+ <% else %>
384
+ <!-- Navigation (Original Top Nav) -->
198
385
  <header class="w-full flex justify-center p-4 sticky top-0 z-50">
199
386
  <nav class="glass-panel w-full max-w-4xl px-8 py-4 flex items-center justify-between">
200
387
  <div class="flex items-center gap-3">
@@ -203,6 +390,9 @@
203
390
 
204
391
  <ul class="hidden md:flex items-center gap-8">
205
392
  <li><a href="/" class="nav-item <%= req.path == '/' ? 'active' : '' %>">Home</a></li>
393
+ <% if FEATURES_CONFIG['type'] == 'e_commerce' %>
394
+ <li><a href="/cart" class="nav-item <%= req.path == '/cart' ? 'active' : '' %>"><i class="fas fa-shopping-cart mr-1"></i> Cart</a></li>
395
+ <% end %>
206
396
  <% Page.where(is_nav: true, is_active: true).all.each do |p| %>
207
397
  <li><a href="/<%= p.slug %>" class="nav-item <%= req.path == "/#{p.slug}" ? 'active' : '' %>"><%= p.title %></a></li>
208
398
  <% end %>
@@ -210,20 +400,64 @@
210
400
  </ul>
211
401
 
212
402
  <div class="flex items-center gap-4">
213
- <a href="https://github.com" class="text-slate-400 hover:text-primary transition-colors"><i class="fab fa-github text-xl"></i></a>
214
- <button class="md:hidden text-slate-400"><i class="fas fa-bars text-xl"></i></button>
403
+ <a href="https://github.com/IshikawaUta/one-for-all-framework" class="text-slate-400 hover:text-primary transition-colors"><i class="fab fa-github text-xl"></i></a>
404
+ <button id="mobile-menu-btn" class="md:hidden text-slate-400 hover:text-primary transition-colors">
405
+ <i class="fas fa-bars text-xl"></i>
406
+ </button>
215
407
  </div>
216
408
  </nav>
217
409
  </header>
218
410
 
219
- <main class="container mx-auto max-w-4xl px-6 py-12">
411
+
412
+ <main class="container mx-auto max-w-6xl px-6 py-12">
220
413
  <%= @content %>
414
+
415
+ <!-- Footer -->
416
+ <footer class="w-full py-12 mt-20 border-t border-white/5 text-center text-slate-500 text-sm">
417
+ <p>&copy; <%= Time.now.year %> One-For-All Framework.</p>
418
+ </footer>
221
419
  </main>
420
+ <% end %>
421
+
422
+ <!-- Mobile Menu Overlay (Shared) -->
423
+ <div id="mobile-menu" class="fixed inset-0 z-[100] hidden">
424
+ <div id="mobile-menu-backdrop" class="absolute inset-0 bg-black/40 backdrop-blur-sm opacity-0"></div>
425
+ <div id="mobile-menu-content" class="absolute right-0 top-0 bottom-0 w-72 glass-panel !rounded-none border-l <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'border-black/5' : 'border-white/10' %> p-8 flex flex-col translate-x-full">
426
+ <div class="flex justify-between items-center mb-10">
427
+ <span class="text-xl font-black tracking-tighter">OFA<span class="text-primary">.menu</span></span>
428
+ <button id="mobile-menu-close" class="text-slate-400 hover:text-red-400 transition-colors">
429
+ <i class="fas fa-times text-2xl"></i>
430
+ </button>
431
+ </div>
432
+
433
+ <nav class="flex flex-col gap-6">
434
+ <a href="/" class="text-lg font-bold <%= req.path == '/' ? 'text-primary' : 'text-slate-500' %>">
435
+ <i class="fas fa-home w-8"></i> Home
436
+ </a>
437
+ <% if FEATURES_CONFIG['type'] == 'e_commerce' %>
438
+ <a href="/cart" class="text-lg font-bold <%= req.path == '/cart' ? 'text-primary' : 'text-slate-500' %>">
439
+ <i class="fas fa-shopping-cart w-8"></i> Cart
440
+ </a>
441
+ <% end %>
442
+ <% Page.where(is_nav: true, is_active: true).all.each do |p| %>
443
+ <a href="/<%= p.slug %>" class="text-lg font-bold <%= req.path == "/#{p.slug}" ? 'text-primary' : 'text-slate-500' %>">
444
+ <i class="fas fa-file-alt w-8"></i> <%= p.title %>
445
+ </a>
446
+ <% end %>
447
+ <div class="h-px <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'bg-black/10' : 'bg-white/5' %> my-2"></div>
448
+ <a href="/dashboard" class="text-lg font-bold <%= req.path.start_with?('/dashboard') ? 'text-primary' : (['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'text-slate-600' : 'text-slate-500') %>">
449
+ <i class="fas fa-gauge-high w-8"></i> Dashboard
450
+ </a>
451
+ </nav>
452
+
453
+ <div class="mt-auto pt-10 border-t <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'border-black/10' : 'border-white/5' %> flex gap-6 justify-center">
454
+ <a href="#" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-twitter"></i></a>
455
+ <a href="#" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-discord"></i></a>
456
+ <a href="https://github.com/IshikawaUta/one-for-all-framework" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-github"></i></a>
457
+ </div>
458
+ </div>
459
+ </div>
222
460
 
223
- <!-- Footer -->
224
- <footer class="w-full py-12 mt-20 border-t border-white/5 text-center text-slate-500 text-sm">
225
- <p>&copy; <%= Time.now.year %> One-For-All Framework.</p>
226
- </footer>
227
461
 
228
462
  <script>
229
463
  document.addEventListener('DOMContentLoaded', () => {
@@ -244,6 +478,51 @@
244
478
  duration: 1000,
245
479
  easing: 'easeOutCubic'
246
480
  });
481
+
482
+ // Mobile Menu Logic
483
+ const menuBtn = document.getElementById('mobile-menu-btn');
484
+ const menuClose = document.getElementById('mobile-menu-close');
485
+ const menu = document.getElementById('mobile-menu');
486
+ const backdrop = document.getElementById('mobile-menu-backdrop');
487
+ const content = document.getElementById('mobile-menu-content');
488
+ let isMenuOpen = false;
489
+
490
+ const toggleMenu = () => {
491
+ isMenuOpen = !isMenuOpen;
492
+ if (isMenuOpen) {
493
+ menu.classList.remove('hidden');
494
+ anime({
495
+ targets: backdrop,
496
+ opacity: [0, 1],
497
+ duration: 400,
498
+ easing: 'easeOutQuad'
499
+ });
500
+ anime({
501
+ targets: content,
502
+ translateX: ['100%', '0%'],
503
+ duration: 600,
504
+ easing: 'easeOutExpo'
505
+ });
506
+ } else {
507
+ anime({
508
+ targets: backdrop,
509
+ opacity: 0,
510
+ duration: 300,
511
+ easing: 'easeInQuad'
512
+ });
513
+ anime({
514
+ targets: content,
515
+ translateX: '100%',
516
+ duration: 400,
517
+ easing: 'easeInExpo',
518
+ complete: () => menu.classList.add('hidden')
519
+ });
520
+ }
521
+ };
522
+
523
+ menuBtn.addEventListener('click', toggleMenu);
524
+ menuClose.addEventListener('click', toggleMenu);
525
+ backdrop.addEventListener('click', toggleMenu);
247
526
  });
248
527
  </script>
249
528
  </body>
data/app/views/login.erb CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  <div class="text-center pt-4 border-t border-white/5">
40
40
  <p class="text-sm text-slate-500 dark:text-slate-400">
41
- Forgot password? <a href="#" class="text-primary font-bold hover:underline">Contact System Admin</a>
41
+ Forgot password? <a href="mailto:komikers09@gmail.com" class="text-primary font-bold hover:underline">Contact System Admin</a>
42
42
  </p>
43
43
  </div>
44
44
  </div>
@@ -0,0 +1,42 @@
1
+ <div class="mb-8">
2
+ <a href="/products" class="text-slate-400 hover:text-primary transition-colors">
3
+ <i class="fas fa-arrow-left mr-2"></i> Back to Shop
4
+ </a>
5
+ </div>
6
+
7
+ <div class="glass-panel p-8 md:p-12">
8
+ <div class="flex flex-col md:flex-row gap-12">
9
+ <div class="w-full md:w-1/2 aspect-square rounded-3xl overflow-hidden bg-slate-900/50 shadow-2xl">
10
+ <img src="<%= @product.image_url || '/images/logo.png' %>" class="w-full h-full object-cover">
11
+ </div>
12
+
13
+ <div class="w-full md:w-1/2 flex flex-col justify-center">
14
+ <div class="badge-premium"><%= @product.category || 'General' %></div>
15
+ <h1 class="text-4xl md:text-5xl font-black mb-4 tracking-tighter"><%= @product.name %></h1>
16
+ <div class="text-3xl font-bold text-primary mb-6">$<%= @product.price %></div>
17
+
18
+ <div class="markdown-content mb-10">
19
+ <%= markdown(@product.description) %>
20
+ </div>
21
+
22
+ <div class="flex items-center gap-6">
23
+ <form action="/cart/add" method="POST" class="flex items-center gap-4">
24
+ <%= csrf_tag %>
25
+ <input type="hidden" name="product_id" value="<%= @product.id %>">
26
+ <div class="flex items-center glass-panel !rounded-xl overflow-hidden">
27
+ <button type="button" onclick="this.nextElementSibling.stepDown()" class="px-4 py-2 hover:bg-white/5">-</button>
28
+ <input type="number" name="qty" value="1" min="1" class="w-12 bg-transparent text-center font-bold focus:outline-none">
29
+ <button type="button" onclick="this.previousElementSibling.stepUp()" class="px-4 py-2 hover:bg-white/5">+</button>
30
+ </div>
31
+ <button type="submit" class="btn-premium flex-1">
32
+ <i class="fas fa-shopping-cart mr-2"></i> Add to Cart
33
+ </button>
34
+ </form>
35
+ </div>
36
+
37
+ <div class="mt-8 pt-8 border-t border-white/5 text-sm text-slate-500">
38
+ <i class="fas fa-cubes mr-2"></i> Stock available: <%= @product.stock %> items
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
@@ -0,0 +1,40 @@
1
+ <div class="text-center mb-16">
2
+ <div class="badge-premium">E-Commerce Mode</div>
3
+ <h1 class="text-5xl font-black mb-4 tracking-tighter">Premium Shop</h1>
4
+ <p class="text-slate-500 text-lg">Curated collection of state-of-the-art digital assets.</p>
5
+ </div>
6
+
7
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
8
+ <% @products.each do |product| %>
9
+ <div class="glass-card group">
10
+ <div class="card-glow"></div>
11
+ <div class="aspect-video mb-6 rounded-2xl overflow-hidden bg-slate-900/50 relative">
12
+ <img src="<%= product.image_url || '/images/logo.png' %>" class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700">
13
+ <div class="absolute top-4 right-4 bg-primary text-white font-black px-4 py-2 rounded-xl shadow-xl">
14
+ $<%= product.price %>
15
+ </div>
16
+ </div>
17
+ <h3 class="text-2xl font-black mb-2"><%= product.name %></h3>
18
+ <p class="text-slate-500 mb-6 line-clamp-2"><%= product.description %></p>
19
+
20
+ <div class="flex items-center justify-between mt-auto">
21
+ <a href="/products/<%= product.slug %>" class="btn-secondary">Details</a>
22
+ <form action="/cart/add" method="POST">
23
+ <%= csrf_tag %>
24
+ <input type="hidden" name="product_id" value="<%= product.id %>">
25
+ <button type="submit" class="btn-premium">
26
+ <i class="fas fa-shopping-cart mr-2"></i> Add to Cart
27
+ </button>
28
+ </form>
29
+ </div>
30
+ </div>
31
+ <% end %>
32
+
33
+ <% if @products.empty? %>
34
+ <div class="col-span-full glass-card text-center py-20">
35
+ <i class="fas fa-box-open text-6xl text-slate-300 mb-6"></i>
36
+ <h2 class="text-2xl font-bold text-slate-400">No products available yet.</h2>
37
+ <p class="text-slate-500">Visit dashboard to add your first product.</p>
38
+ </div>
39
+ <% end %>
40
+ </div>
data/bin/ofa CHANGED
@@ -17,6 +17,13 @@ rescue LoadError
17
17
  end
18
18
 
19
19
  def help
20
+ puts " "
21
+ puts " ____ _______ ___ "
22
+ puts " / __ \\/ ____/ / | "
23
+ puts " / / / / /_ / /| | Framework "
24
+ puts "/ /_/ / __/ / ___ | Premium MVC "
25
+ puts "\\____/_/ /_/ |_| v2.0.0 "
26
+ puts " "
20
27
  puts "✨ One-For-All Framework CLI ✨"
21
28
  puts "-----------------------------"
22
29
  puts "Usage:"
@@ -27,8 +34,8 @@ def help
27
34
  puts " ofa g migration NAME - Generate a new migration"
28
35
  puts " ofa g post TITLE - Create a new post [args: --category, --author, --image]"
29
36
  puts " ofa feature ACTION F - Toggle features: action=enable/disable, F=cms/auth"
30
- puts " ofa type NAME - Set application type: portfolio, blog, landing_page"
31
- puts " ofa theme NAME - Set UI theme: light_glass, dark_glass"
37
+ puts " ofa type NAME - Set application type: portfolio, blog, landing_page, e_commerce"
38
+ puts " ofa theme NAME - Set UI theme: light_glass, dark_glass, cyber_sidebar, retro_terminal, light_sidebar"
32
39
  puts " ofa storage NAME - Set image storage: local, cloudinary"
33
40
  puts " ofa reset-password USR PWD - Reset admin account password"
34
41
  puts " ofa db switch TYPE [NAME] - Switch DB: sqlite, mysql, mariadb, mongodb, postgres"
@@ -42,6 +49,7 @@ end
42
49
 
43
50
  def run_migrations
44
51
  Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
52
+ ENV['SKIP_MODELS'] = '1'
45
53
  require File.join(FRAMEWORK_ROOT, 'config', 'boot')
46
54
  puts "Running migrations..."
47
55
  migration_path = File.join(PROJECT_ROOT, "db/migrations")
@@ -101,6 +109,13 @@ when 'new'
101
109
 
102
110
  when 'init'
103
111
  app_type = ARGV.shift || 'landing_page'
112
+ puts " "
113
+ puts " ____ _______ ___ "
114
+ puts " / __ \\/ ____/ / | "
115
+ puts " / / / / /_ / /| | Framework "
116
+ puts "/ /_/ / __/ / ___ | Premium MVC "
117
+ puts "\\____/_/ /_/ |_| v2.0.0 "
118
+ puts " "
104
119
  puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
105
120
 
106
121
  # --- Interactive Wizard ---
@@ -130,7 +145,7 @@ when 'init'
130
145
  puts "-----------------------------\n"
131
146
 
132
147
  # Create directories
133
- dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/img', 'public/img/uploads']
148
+ dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
134
149
  dirs.each { |dir| FileUtils.mkdir_p(File.join(PROJECT_ROOT, dir)) }
135
150
 
136
151
  # Copy framework core migrations
@@ -263,8 +278,6 @@ when 'init'
263
278
  File.write(db_config_path, JSON.pretty_generate(db_default))
264
279
  end
265
280
 
266
- # Premium Scaffolding
267
- puts " Generating premium UI components..."
268
281
  # Copy views from framework to project
269
282
  framework_views = File.join(FRAMEWORK_ROOT, 'app', 'views')
270
283
  project_views = File.join(PROJECT_ROOT, 'app', 'views')
@@ -272,6 +285,14 @@ when 'init'
272
285
  # Recursive copy for views (including cms folder)
273
286
  FileUtils.cp_r(File.join(framework_views, '.'), project_views)
274
287
 
288
+ # Copy public assets from framework to project
289
+ framework_public = File.join(FRAMEWORK_ROOT, 'public')
290
+ project_public = File.join(PROJECT_ROOT, 'public')
291
+ if File.directory?(framework_public)
292
+ # Use cp_r with '.' to copy contents of public, not the public folder itself
293
+ FileUtils.cp_r(File.join(framework_public, '.'), project_public)
294
+ end
295
+
275
296
  # Copy models from framework to project
276
297
  framework_models = File.join(FRAMEWORK_ROOT, 'app', 'models')
277
298
  project_models = File.join(PROJECT_ROOT, 'app', 'models')
@@ -302,6 +323,14 @@ when 'init'
302
323
  def index; end
303
324
  end
304
325
  RUBY
326
+ when 'e_commerce'
327
+ puts " Scaffolding E-Commerce starter..."
328
+ File.write(File.join(PROJECT_ROOT, 'app/controllers/products_controller.rb'), <<~RUBY)
329
+ require_relative 'application_controller'
330
+ class ProductsController < ApplicationController
331
+ def index; end
332
+ end
333
+ RUBY
305
334
  end
306
335
 
307
336
  puts "Done! Run 'ofa run' to start your app."
@@ -376,8 +405,8 @@ when 'feature'
376
405
  when 'type'
377
406
  ensure_initialized!
378
407
  name = ARGV.shift
379
- unless %w[landing_page portfolio blog].include?(name)
380
- puts "❌ Error: Invalid app type '#{name}'. Choose: landing_page, portfolio, blog."
408
+ unless %w[landing_page portfolio blog e_commerce].include?(name)
409
+ puts "❌ Error: Invalid app type '#{name}'. Choose: landing_page, portfolio, blog, e_commerce."
381
410
  exit 1
382
411
  end
383
412
  config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
@@ -389,8 +418,8 @@ when 'type'
389
418
  when 'theme'
390
419
  ensure_initialized!
391
420
  name = ARGV.shift
392
- unless %w[light_glass dark_glass].include?(name)
393
- puts "❌ Error: Invalid theme '#{name}'. Choose: light_glass, dark_glass."
421
+ unless %w[light_glass dark_glass cyber_sidebar retro_terminal light_sidebar].include?(name)
422
+ puts "❌ Error: Invalid theme '#{name}'. Choose: light_glass, dark_glass, cyber_sidebar, retro_terminal, light_sidebar."
394
423
  exit 1
395
424
  end
396
425
  config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
data/config/boot.rb CHANGED
@@ -76,6 +76,7 @@ require_relative 'database'
76
76
  # Autoloading (Framework Core first, then APP_ROOT)
77
77
  framework_app = File.expand_path('../app', __dir__)
78
78
  ['controllers', 'models', 'middleware', 'helpers'].each do |folder|
79
+ next if folder == 'models' && ENV['SKIP_MODELS']
79
80
  loaded = []
80
81
  # Load framework core
81
82
  Dir.glob(File.join(framework_app, folder, '*.rb')).each do |f|
data/config/features.json CHANGED
@@ -2,6 +2,6 @@
2
2
  "auth": true,
3
3
  "cms": true,
4
4
  "type": "landing_page",
5
- "theme": "light_glass",
5
+ "theme": "light_sidebar",
6
6
  "storage": "cloudinary"
7
7
  }