one-for-all-framework 5.2.0 → 5.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ca65e26e9715c8875888a62860db4e427e8b067175b380a83b966c3207dbf5a
4
- data.tar.gz: f3898f1f830732781a4cdf43ca54b812f64c98067b58a2c25884f13afe0430f7
3
+ metadata.gz: 997b63198052b18b0a1f1f74d74829835a408e0efa5b2adc38e795e9753a9576
4
+ data.tar.gz: 7dfe59789c4cef94ab2a49add1d459f3ba2cf1c362ab812238e74643019eb907
5
5
  SHA512:
6
- metadata.gz: b0c527397ec49c0052ce8eb078f12633280d4ece5b5f9c8507bd48f7bece5920d10700bf3266a6c7b085b4c1dd8b4e6870e37da2c34395a49e88c6f6cebd4a0e
7
- data.tar.gz: e99693905ab06c74f4a1f0010e9716a7475c4037b527f4d443e8c91cfbb72c8b6d2bb98445108e743c601181478d6017320e98c7dbcbbd1d02bc9bfb71f03720
6
+ metadata.gz: e4d233bc2974284d2ba9de83f20eb4f900bc70fbae6bdf1fc58dc2d9ca8348dcfb6d9baf675347d002dca1620d9d47d9313e44dd0fcda36ae11a66d7ee7ec6a1
7
+ data.tar.gz: 8d634a30437413830ce93d54daf1a3dbed0f27cb1ff46e88fe1bc903435048f0873cdba4899e5f7e1e2933bcb318cc677c1d682a6be551067bcb13a3d67074d7
data/Dockerfile CHANGED
@@ -16,4 +16,4 @@ RUN bundle install
16
16
  EXPOSE 3000
17
17
 
18
18
  # Start command
19
- CMD ["./ofa", "run"]
19
+ CMD ["bundle", "exec", "ofa", "run"]
data/Procfile CHANGED
@@ -1 +1 @@
1
- web: ./ofa run
1
+ web: bundle exec ofa run
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="public/images/logo.png" width="500" height="500" alt="OFA Framework Logo">
3
3
  </p>
4
4
 
5
- # ⚡ One-For-All (OFA) Framework v5.2.0
5
+ # ⚡ One-For-All (OFA) Framework v5.3.0
6
6
 
7
7
  [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.0.0-red.svg)](https://www.ruby-lang.org/)
8
8
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
@@ -22,6 +22,7 @@ class AuthController < ApplicationController
22
22
  session['username'] = user.username
23
23
  session['last_active_at'] = Time.now.to_i
24
24
 
25
+ log_activity("User logged in: #{user.username}")
25
26
  redirect_to '/dashboard'
26
27
  else
27
28
  render 'login', title: 'Login - One-For-All', error: 'Invalid credentials'
@@ -29,6 +30,7 @@ class AuthController < ApplicationController
29
30
  end
30
31
 
31
32
  def logout
33
+ log_activity("User logged out: #{session['username']}")
32
34
  ['user_id', :user_id, 'username', :username, 'last_active_at', :last_active_at].each { |k| session.delete(k) }
33
35
  redirect_to '/login'
34
36
  end
@@ -20,6 +20,7 @@ class ProductsController < ApplicationController
20
20
  data = params['product']
21
21
  @product = Product.new(data)
22
22
  if @product.save
23
+ log_activity("Created product: #{@product.name}", @product, "Price: #{@product.price}")
23
24
  redirect_to '/dashboard/products'
24
25
  else
25
26
  render 'cms/products_form'
@@ -33,7 +34,9 @@ class ProductsController < ApplicationController
33
34
 
34
35
  def update
35
36
  @product = Product[params['id']]
36
- if @product.update(params['product'])
37
+ data = params['product']
38
+ if @product.update(data)
39
+ log_activity("Updated product: #{@product.name}", @product, "New Data: #{data.to_json}")
37
40
  redirect_to '/dashboard/products'
38
41
  else
39
42
  render 'cms/products_form'
@@ -42,7 +45,9 @@ class ProductsController < ApplicationController
42
45
 
43
46
  def destroy
44
47
  @product = Product[params['id']]
48
+ name = @product.name
45
49
  @product.destroy
50
+ log_activity("Deleted product: #{name}")
46
51
  redirect_to '/dashboard/products'
47
52
  end
48
53
 
@@ -16,6 +16,7 @@ class ProjectsController < ApplicationController
16
16
  data['is_active'] = boolean_param(data['is_active'])
17
17
  @project = Project.new(data)
18
18
  if @project.save
19
+ log_activity("Created project: #{@project.title}", @project, "Link: #{@project.link}")
19
20
  redirect_to '/dashboard/projects'
20
21
  else
21
22
  render 'cms/projects_form'
@@ -32,6 +33,7 @@ class ProjectsController < ApplicationController
32
33
  data = params['project']
33
34
  data['is_active'] = boolean_param(data['is_active'])
34
35
  if @project.update(data)
36
+ log_activity("Updated project: #{@project.title}", @project, "New Data: #{data.to_json}")
35
37
  redirect_to '/dashboard/projects'
36
38
  else
37
39
  render 'cms/projects_form'
@@ -42,7 +44,9 @@ class ProjectsController < ApplicationController
42
44
  @project = Project[params['id']]
43
45
  delete_from_storage(@project.image_url) if @project.respond_to?(:image_url)
44
46
  delete_all_images_from_content(@project.description) if @project.respond_to?(:description)
47
+ title = @project.title
45
48
  @project.destroy
49
+ log_activity("Deleted project: #{title}")
46
50
  redirect_to '/dashboard/projects'
47
51
  end
48
52
  end
data/app/views/docs.erb CHANGED
@@ -1,6 +1,6 @@
1
1
  <div class="space-y-12 pb-20">
2
2
  <div class="text-left">
3
- <div class="badge-premium">Documentation v5.2.0</div>
3
+ <div class="badge-premium">Documentation v5.3.0</div>
4
4
  <h1 class="text-5xl font-black tracking-tighter mb-4 text-slate-700 dark:text-white">Framework <span class="text-primary">Guide</span></h1>
5
5
  <p class="text-xl text-slate-500 max-w-2xl leading-relaxed">Everything you need to know about building premium web applications with the One-For-All framework.</p>
6
6
  </div>
data/app/views/index.erb CHANGED
@@ -1,6 +1,6 @@
1
1
  <div class="text-center space-y-6 max-w-2xl mx-auto py-12">
2
2
  <div class="inline-flex items-center px-4 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider animate-pulse">
3
- Framework Version 5.2.0
3
+ Framework Version 5.3.0
4
4
  </div>
5
5
 
6
6
  <h1 class="text-5xl md:text-7xl font-black tracking-tight leading-tight">
data/app/views/layout.erb CHANGED
@@ -283,33 +283,100 @@
283
283
 
284
284
  .nav-item {
285
285
  color: #64748b;
286
- font-weight: 600;
287
- transition: color 0.3s;
286
+ font-weight: 700;
287
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
288
+ position: relative;
289
+ padding: 0.5rem 0;
290
+ display: inline-flex;
291
+ align-items: center;
292
+ }
293
+
294
+ .nav-item::after {
295
+ content: '';
296
+ position: absolute;
297
+ bottom: 0;
298
+ left: 0;
299
+ width: 0;
300
+ height: 2px;
301
+ background: linear-gradient(90deg, var(--primary), var(--secondary));
302
+ transition: width 0.3s ease;
303
+ border-radius: 99px;
304
+ }
305
+
306
+ .nav-item:hover::after, .nav-item.active::after {
307
+ width: 100%;
288
308
  }
289
309
 
290
310
  .nav-item:hover, .nav-item.active {
291
311
  color: var(--primary);
312
+ transform: translateY(-1px);
292
313
  }
293
314
 
294
315
  /* Markdown Overrides */
295
- .markdown-content { text-align: left; line-height: 1.6; }
296
- .markdown-content h1 { font-size: 2.25rem; font-weight: 800; margin: 2rem 0 1rem; color: var(--primary); }
297
- .markdown-content p { font-size: 1.125rem; color: #475569; margin-bottom: 1.5rem; }
316
+ .markdown-content { text-align: left; line-height: 1.8; color: #475569; overflow-x: auto; }
317
+ .markdown-content h1 { font-size: 2.5rem; font-weight: 900; margin: 2.5rem 0 1.5rem; color: var(--primary); letter-spacing: -0.02em; line-height: 1.2; }
318
+ .markdown-content h2 { font-size: 2rem; font-weight: 800; margin: 2.5rem 0 1.25rem; color: var(--primary); letter-spacing: -0.01em; border-bottom: 1px solid rgba(0,0,0,0.05); padding-bottom: 0.5rem; }
319
+ .markdown-content h3 { font-size: 1.5rem; font-weight: 800; margin: 2rem 0 1rem; color: var(--secondary); }
320
+ .markdown-content h4 { font-size: 1.25rem; font-weight: 700; margin: 1.5rem 0 0.75rem; color: var(--primary); }
321
+ .markdown-content p { font-size: 1.1rem; margin-bottom: 1.5rem; }
298
322
  .markdown-content ul { list-style-type: disc !important; margin-left: 1.5rem !important; margin-bottom: 1.5rem !important; display: block !important; }
299
323
  .markdown-content ol { list-style-type: decimal !important; margin-left: 1.5rem !important; margin-bottom: 1.5rem !important; display: block !important; }
300
324
  .markdown-content li { margin-bottom: 0.5rem !important; display: list-item !important; }
301
- .markdown-content blockquote { border-left: 4px solid var(--primary); padding: 1rem 0 1rem 1.5rem; font-style: italic; margin: 1.5rem 0; color: #64748b; background: rgba(255, 255, 255, 0.03); }
302
- .markdown-content pre { background: rgba(0,0,0,0.05); padding: 1.25rem; border-radius: 1rem; overflow-x: auto; font-family: 'Fira Code', monospace; margin: 1.5rem 0; line-height: 1.4; border: 1px solid rgba(0,0,0,0.1); }
303
- .markdown-content code { background: rgba(0,0,0,0.05); padding: 0.2rem 0.4rem; border-radius: 0.4rem; font-family: 'Fira Code', monospace; font-size: 0.9em; }
304
- .markdown-content figcaption { text-align: center; color: #64748b; opacity: 0.6; font-size: 0.85rem; margin-top: 0.75rem; font-style: italic; line-height: 1.4; }
305
- .markdown-content a { color: var(--primary); font-weight: 700; text-decoration: none; border-bottom: 1px dashed var(--primary); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
306
- .markdown-content a:hover { color: var(--secondary); border-bottom: 1px solid var(--secondary); opacity: 0.8; }
325
+ .markdown-content blockquote { border-left: 4px solid var(--primary); padding: 1rem 1.5rem; font-style: italic; margin: 2rem 0; color: #64748b; background: rgba(var(--primary-rgb), 0.05); border-radius: 0 1rem 1rem 0; }
326
+ .markdown-content pre { background: #0f172a; padding: 1.5rem; border-radius: 1.25rem; overflow-x: auto; font-family: 'Fira Code', monospace; margin: 2rem 0; line-height: 1.5; border: 1px solid rgba(255,255,255,0.1); box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5); }
327
+ .markdown-content code { background: rgba(0,0,0,0.05); padding: 0.2rem 0.4rem; border-radius: 0.4rem; font-family: 'Fira Code', monospace; font-size: 0.9em; color: var(--primary); font-weight: 600; }
328
+ .markdown-content img { border-radius: 1.5rem; margin: 2rem auto; display: block; border: 1px solid rgba(0,0,0,0.05); transition: transform 0.3s ease; }
329
+ .markdown-content img:hover { transform: scale(1.01); }
330
+ .markdown-content table {
331
+ width: 100%;
332
+ border-collapse: separate;
333
+ border-spacing: 0;
334
+ margin: 2.5rem 0;
335
+ border: 1px solid rgba(0,0,0,0.1);
336
+ border-radius: 1.5rem;
337
+ overflow: hidden;
338
+ background: rgba(255, 255, 255, 0.4);
339
+ backdrop-filter: blur(10px);
340
+ box-shadow: 0 10px 30px -10px rgba(0,0,0,0.1);
341
+ }
342
+ .markdown-content th {
343
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
344
+ color: white !important;
345
+ padding: 1.25rem 1.5rem;
346
+ font-weight: 800;
347
+ text-transform: uppercase;
348
+ font-size: 0.7rem;
349
+ text-align: left !important;
350
+ letter-spacing: 0.1em;
351
+ border: none;
352
+ }
353
+ .markdown-content td {
354
+ padding: 1.25rem 1.5rem;
355
+ border-bottom: 1px solid rgba(0,0,0,0.05);
356
+ font-size: 0.9rem;
357
+ color: #475569;
358
+ vertical-align: top;
359
+ line-height: 1.6;
360
+ }
361
+ .markdown-content tr:last-child td { border-bottom: none; }
362
+ .markdown-content tr:nth-child(even) { background: rgba(0,0,0,0.02); }
363
+ .markdown-content tr:hover { background: rgba(0,0,0,0.04); transition: background 0.3s; }
364
+ .markdown-content code { white-space: pre-wrap; word-break: break-word; }
365
+ .markdown-content pre code { white-space: pre; word-break: normal; }
366
+ .markdown-content a { color: var(--primary); font-weight: 700; text-decoration: none; border-bottom: 1px dashed var(--primary); transition: all 0.3s; }
367
+ .markdown-content a:hover { color: var(--secondary); border-bottom-style: solid; }
307
368
 
308
- .dark .markdown-content p { color: #cbd5e1; }
309
- .dark .markdown-content ul, .dark .markdown-content ol { color: #cbd5e1; }
310
- .dark .markdown-content blockquote { color: #94a3b8; background: rgba(255, 255, 255, 0.03); }
311
- .dark .markdown-content pre { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.1); color: #e2e8f0; }
312
- .dark .markdown-content code { background: rgba(255,255,255,0.1); color: var(--primary); }
369
+ .dark .markdown-content table {
370
+ background: rgba(15, 23, 42, 0.6);
371
+ border-color: rgba(255,255,255,0.1);
372
+ box-shadow: 0 20px 40px -12px rgba(0,0,0,0.5);
373
+ }
374
+ .dark .markdown-content td {
375
+ border-color: rgba(255,255,255,0.05);
376
+ color: #94a3b8;
377
+ }
378
+ .dark .markdown-content tr:nth-child(even) { background: rgba(255,255,255,0.03); }
379
+ .dark .markdown-content tr:hover { background: rgba(255,255,255,0.05); }
313
380
 
314
381
  /* Code Copy Button */
315
382
  .code-wrapper { position: relative; margin: 1.5rem 0; }
@@ -498,7 +565,7 @@
498
565
  </header>
499
566
 
500
567
  <!-- Main Content Area -->
501
- <main class="flex-1 md:ml-64 p-6 md:p-12 pt-24 md:pt-12">
568
+ <main class="flex-1 md:ml-64 p-4 md:p-12 pt-24 md:pt-12 min-w-0 w-full">
502
569
  <div class="max-w-5xl mx-auto">
503
570
  <%= @content %>
504
571
  </div>
@@ -518,14 +585,14 @@
518
585
  </div>
519
586
 
520
587
  <ul class="hidden md:flex items-center gap-8">
521
- <li><a href="/" class="nav-item <%= req.path == '/' ? 'active' : '' %>">Home</a></li>
588
+ <li><a href="/" class="nav-item <%= req.path == '/' ? 'active' : '' %>"><i class="fas fa-house mr-2 text-sm opacity-70"></i> Home</a></li>
522
589
  <% if FEATURES_CONFIG['type'] == 'e_commerce' %>
523
- <li><a href="/cart" class="nav-item <%= req.path == '/cart' ? 'active' : '' %>"><i class="fas fa-shopping-cart mr-1"></i> Cart</a></li>
590
+ <li><a href="/cart" class="nav-item <%= req.path == '/cart' ? 'active' : '' %>"><i class="fas fa-shopping-cart mr-2 text-sm opacity-70"></i> Cart</a></li>
524
591
  <% end %>
525
592
  <% Page.where(is_nav: true, is_active: true).all.each do |p| %>
526
- <li><a href="/<%= p.slug %>" class="nav-item <%= req.path == "/#{p.slug}" ? 'active' : '' %>"><%= p.title %></a></li>
593
+ <li><a href="/<%= p.slug %>" class="nav-item <%= req.path == "/#{p.slug}" ? 'active' : '' %>"><i class="fas fa-file-lines mr-2 text-sm opacity-70"></i> <%= p.title %></a></li>
527
594
  <% end %>
528
- <li><a href="/dashboard" class="nav-item <%= req.path.start_with?('/dashboard') ? 'active' : '' %>">Dashboard</a></li>
595
+ <li><a href="/dashboard" class="nav-item <%= req.path.start_with?('/dashboard') ? 'active' : '' %>"><i class="fas fa-gauge-high mr-2 text-sm opacity-70"></i> Dashboard</a></li>
529
596
  </ul>
530
597
 
531
598
  <div class="flex items-center gap-4">
@@ -561,7 +628,7 @@
561
628
 
562
629
  <nav class="flex flex-col gap-6">
563
630
  <a href="/" class="text-lg font-bold <%= req.path == '/' ? 'text-primary' : 'text-slate-500' %>">
564
- <i class="fas fa-home w-8"></i> Home
631
+ <i class="fas fa-house w-8"></i> Home
565
632
  </a>
566
633
  <% if FEATURES_CONFIG['type'] == 'e_commerce' %>
567
634
  <a href="/cart" class="text-lg font-bold <%= req.path == '/cart' ? 'text-primary' : 'text-slate-500' %>">
@@ -570,7 +637,7 @@
570
637
  <% end %>
571
638
  <% Page.where(is_nav: true, is_active: true).all.each do |p| %>
572
639
  <a href="/<%= p.slug %>" class="text-lg font-bold <%= req.path == "/#{p.slug}" ? 'text-primary' : 'text-slate-500' %>">
573
- <i class="fas fa-file-alt w-8"></i> <%= p.title %>
640
+ <i class="fas fa-file-lines w-8"></i> <%= p.title %>
574
641
  </a>
575
642
  <% end %>
576
643
  <div class="h-px <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'bg-black/10' : 'bg-white/5' %> my-2"></div>
data/app/views/page.erb CHANGED
@@ -1,11 +1,17 @@
1
- <div class="glass-card anime-element">
2
- <h1><%= @title %></h1>
3
- <div class="page-content markdown-content" style="margin-top: 2rem;">
1
+ <div class="glass-card anime-element !p-5 md:!p-12 text-left max-w-4xl mx-auto">
2
+ <div class="mb-8 md:mb-12">
3
+ <div class="badge-premium">Halaman Statis</div>
4
+ <h1 class="text-2xl md:text-5xl font-black mb-4 leading-tight"><%= @title %></h1>
5
+ <div class="h-1 w-20 bg-primary rounded-full"></div>
6
+ </div>
7
+
8
+ <div class="page-content markdown-content">
4
9
  <%= markdown(@content) %>
5
10
  </div>
11
+
6
12
  <div class="mt-12 pt-8 border-t border-white/5">
7
- <a href="/" class="btn-secondary">
8
- <i class="fas fa-arrow-left mr-2"></i> Back to Home
13
+ <a href="/" class="btn-secondary w-full sm:w-auto">
14
+ <i class="fas fa-arrow-left mr-2"></i> Kembali ke Beranda
9
15
  </a>
10
16
  </div>
11
17
  </div>
data/app/views/post.erb CHANGED
@@ -1,14 +1,17 @@
1
- <div class="glass-card anime-element" style="text-align: left; max-width: 800px; margin: 0 auto;">
2
- <div style="margin-bottom: 2rem;">
3
- <span class="badge" style="background: var(--primary); color: white; padding: 0.3rem 0.8rem; border-radius: 0.5rem; font-size: 0.8rem; font-weight: 700;">
4
- <%= @post.category.upcase %>
5
- </span>
6
- <h1 style="margin: 1rem 0 0.5rem 0; font-size: 3rem; line-height: 1.1;"><%= @post.title %></h1>
7
- <p style="color: var(--text-muted); font-size: 0.95rem;">Published on <%= @post.created_at.strftime('%B %d, %Y') %></p>
1
+ <div class="glass-card anime-element !p-6 md:!p-12 text-left max-w-4xl mx-auto">
2
+ <div class="mb-8 md:mb-12">
3
+ <div class="badge-premium"><%= @post.category %></div>
4
+ <h1 class="text-3xl md:text-5xl font-black mb-4 leading-tight"><%= @post.title %></h1>
5
+ <p class="text-slate-500 dark:text-slate-400 text-sm md:text-base flex items-center gap-2">
6
+ <i class="far fa-calendar-alt"></i> Diterbitkan pada <%= @post.created_at.strftime('%d %B %Y') %>
7
+ </p>
8
8
  </div>
9
9
 
10
10
  <% if @post.image_url && !@post.image_url.empty? %>
11
- <img src="<%= @post.image_url %>" alt="<%= @post.title %>" style="width: 100%; max-height: 400px; object-fit: cover; border-radius: 1.5rem; margin-bottom: 2.5rem; border: 1px solid var(--border);">
11
+ <div class="relative rounded-2xl md:rounded-3xl overflow-hidden mb-8 md:mb-12 border border-white/10">
12
+ <img src="<%= @post.image_url %>" alt="<%= @post.title %>" class="w-full h-auto max-h-[500px] object-cover">
13
+ <div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
14
+ </div>
12
15
  <% end %>
13
16
 
14
17
  <div class="post-content markdown-content">
@@ -16,8 +19,8 @@
16
19
  </div>
17
20
 
18
21
  <div class="mt-12 pt-8 border-t border-white/5">
19
- <a href="/" class="btn-secondary">
20
- <i class="fas fa-arrow-left mr-2"></i> Back to Blog
22
+ <a href="/" class="btn-secondary w-full sm:w-auto">
23
+ <i class="fas fa-arrow-left mr-2"></i> Kembali ke Blog
21
24
  </a>
22
25
  </div>
23
26
  </div>
@@ -1,14 +1,20 @@
1
- <div class="glass-card anime-element" style="text-align: left; max-width: 800px; margin: 0 auto;">
2
- <div style="margin-bottom: 2rem;">
3
- <h1 style="margin: 0 0 0.5rem 0; font-size: 3rem; line-height: 1.1;"><%= @project.title %></h1>
4
- <p style="color: var(--text-muted); font-size: 0.95rem;">Project created on <%= @project.created_at.strftime('%B %d, %Y') %></p>
1
+ <div class="glass-card anime-element !p-5 md:!p-12 text-left max-w-4xl mx-auto">
2
+ <div class="mb-8 md:mb-12">
3
+ <div class="badge-premium">Detail Proyek</div>
4
+ <h1 class="text-2xl md:text-5xl font-black mb-4 leading-tight"><%= @project.title %></h1>
5
+ <p class="text-slate-500 dark:text-slate-400 text-sm md:text-base flex items-center gap-2">
6
+ <i class="far fa-calendar-alt"></i> Dibuat pada <%= @project.created_at.strftime('%d %B %Y') %>
7
+ </p>
5
8
  </div>
6
9
 
7
10
  <% if @project.image_url && !@project.image_url.empty? %>
8
- <img src="<%= @project.image_url %>" alt="<%= @project.title %>" style="width: 100%; max-height: 400px; object-fit: cover; border-radius: 1.5rem; margin-bottom: 2.5rem; border: 1px solid var(--border);">
11
+ <div class="relative rounded-2xl md:rounded-3xl overflow-hidden mb-8 md:mb-12 border border-white/10">
12
+ <img src="<%= @project.image_url %>" alt="<%= @project.title %>" class="w-full h-auto max-h-[500px] object-cover">
13
+ <div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
14
+ </div>
9
15
  <% else %>
10
- <div style="width: 100%; height: 300px; background: linear-gradient(135deg, var(--primary), var(--accent)); border-radius: 1.5rem; margin-bottom: 2.5rem; opacity: 0.8; display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem; font-weight: 800;">
11
- <%= @project.title %>
16
+ <div class="w-full aspect-video bg-gradient-to-br from-primary/20 to-secondary/20 rounded-2xl md:rounded-3xl mb-8 md:mb-12 flex items-center justify-center border border-white/10">
17
+ <i class="fas fa-project-diagram text-4xl md:text-6xl text-primary/40"></i>
12
18
  </div>
13
19
  <% end %>
14
20
 
@@ -16,14 +22,14 @@
16
22
  <%= markdown(@project.description) %>
17
23
  </div>
18
24
 
19
- <div class="flex flex-wrap gap-4 items-center mt-12 pt-8 border-t border-white/5">
25
+ <div class="flex flex-col sm:flex-row gap-4 items-center mt-12 pt-8 border-t border-white/5">
20
26
  <% if @project.link && !@project.link.empty? %>
21
- <a href="<%= @project.link %>" target="_blank" class="btn-premium">
22
- <i class="fas fa-rocket mr-2"></i> Visit Live Demo
27
+ <a href="<%= @project.link %>" target="_blank" class="btn-premium w-full sm:w-auto">
28
+ <i class="fas fa-external-link-alt mr-2"></i> Kunjungi Demo
23
29
  </a>
24
30
  <% end %>
25
- <a href="/" class="btn-secondary">
26
- <i class="fas fa-arrow-left mr-2"></i> Back to Home
31
+ <a href="/" class="btn-secondary w-full sm:w-auto">
32
+ <i class="fas fa-arrow-left mr-2"></i> Kembali ke Beranda
27
33
  </a>
28
34
  </div>
29
35
  </div>
data/bin/ofa CHANGED
@@ -33,7 +33,7 @@ def help
33
33
  puts " / __ \\/ ____/ / | "
34
34
  puts " / / / / /_ / /| | Framework "
35
35
  puts "/ /_/ / __/ / ___ | Premium MVC "
36
- puts "\\____/_/ /_/ |_| v5.2.0 "
36
+ puts "\\____/_/ /_/ |_| v5.3.0 "
37
37
  puts " "
38
38
  puts "✨ One-For-All Framework CLI ✨"
39
39
  puts "-----------------------------"
@@ -249,7 +249,7 @@ when 'init'
249
249
  puts " / __ \\/ ____/ / | "
250
250
  puts " / / / / /_ / /| | Framework "
251
251
  puts "/ /_/ / __/ / ___ | Premium MVC "
252
- puts "\\____/_/ /_/ |_| v5.2.0 "
252
+ puts "\\____/_/ /_/ |_| v5.3.0 "
253
253
  puts " "
254
254
  puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
255
255
 
@@ -307,6 +307,16 @@ when 'init'
307
307
  end
308
308
  end
309
309
 
310
+ # Standard project files
311
+ ['.env.example', '.gitignore', 'Dockerfile', 'LICENSE', 'README.md', 'Procfile'].each do |file|
312
+ src = File.join(FRAMEWORK_ROOT, file)
313
+ dest = File.join(PROJECT_ROOT, file)
314
+ if File.exist?(src)
315
+ FileUtils.cp(src, dest)
316
+ puts " Creating #{file}..."
317
+ end
318
+ end
319
+
310
320
  # Create .env
311
321
  env_content = []
312
322
  env_content << "EKS_ENV=development"
@@ -321,8 +331,16 @@ when 'init'
321
331
  # Generated by One-For-All CLI
322
332
  # Target framework: #{FRAMEWORK_ROOT}
323
333
 
324
- # Add framework to load path
325
- $LOAD_PATH.unshift '#{FRAMEWORK_ROOT}'
334
+ # Add framework to load path dynamically
335
+ require 'bundler/setup'
336
+ framework_spec = Gem.loaded_specs['one-for-all-framework']
337
+ if framework_spec
338
+ $LOAD_PATH.unshift framework_spec.full_gem_path
339
+ else
340
+ # Fallback for local development if not in bundle (though it should be)
341
+ $LOAD_PATH.unshift '#{FRAMEWORK_ROOT}'
342
+ end
343
+
326
344
  APP_ROOT = File.expand_path(__dir__)
327
345
  require 'config/boot'
328
346
 
@@ -347,6 +365,7 @@ when 'init'
347
365
  gemfile_content = <<~RUBY
348
366
  source 'https://rubygems.org'
349
367
 
368
+ gem 'one-for-all-framework', '5.3.0'
350
369
  gem 'eks-cent', '4.0.0'
351
370
  gem 'eksa-server'
352
371
  gem 'sequel'
@@ -862,7 +881,7 @@ when 'console'
862
881
  puts " / __ \\/ ____/ / | "
863
882
  puts " / / / / /_ / /| | Framework "
864
883
  puts "/ /_/ / __/ / ___ | Console (REPL) "
865
- puts "\\____/_/ /_/ |_| v5.2.0 "
884
+ puts "\\____/_/ /_/ |_| v5.3.0 "
866
885
  puts " "
867
886
  puts "✨ Loading environment... (Type 'exit' to quit)"
868
887
 
data/config/boot.rb CHANGED
@@ -1,3 +1,5 @@
1
+ Encoding.default_external = Encoding::UTF_8
2
+ Encoding.default_internal = Encoding::UTF_8
1
3
  require 'bundler/setup'
2
4
  require 'dotenv/load'
3
5
  Bundler.require(:default)
@@ -40,7 +42,7 @@ module EksCent
40
42
  raise "Template not found: #{template_path}"
41
43
  end
42
44
 
43
- template_content = File.read(template_path)
45
+ template_content = File.read(template_path, encoding: 'UTF-8')
44
46
  context = Object.new
45
47
  context.extend(ERB::Util)
46
48
  req = @request
@@ -64,17 +66,20 @@ module EksCent
64
66
 
65
67
  context.define_singleton_method(:session) { req ? (req.env['eks_cent.session'] || req.env['rack.session'] || {}) : {} }
66
68
  context.define_singleton_method(:h) { |s| CGI.escapeHTML(s.to_s) }
67
- locals.each { |k, v| context.instance_variable_set("@#{k}", v) }
69
+ locals.each do |k, v|
70
+ v = v.force_encoding('UTF-8') if v.is_a?(String) && v.encoding == Encoding::BINARY
71
+ context.instance_variable_set("@#{k}", v)
72
+ end
68
73
 
69
- result = ERB.new(template_content).result(context.instance_eval { binding })
74
+ result = ERB.new(template_content).result(context.instance_eval { binding }).force_encoding('UTF-8')
70
75
 
71
76
  if layout
72
77
  layout_name = layout == true ? 'layout' : layout.to_s
73
78
  layout_path = File.join(APP_ROOT, 'app', 'views', "#{layout_name}.erb")
74
79
  if File.file?(layout_path)
75
80
  context.instance_variable_set("@content", result)
76
- layout_content = File.read(layout_path)
77
- result = ERB.new(layout_content).result(context.instance_eval { binding })
81
+ layout_content = File.read(layout_path, encoding: 'UTF-8')
82
+ result = ERB.new(layout_content).result(context.instance_eval { binding }).force_encoding('UTF-8')
78
83
  end
79
84
  end
80
85
 
data/config/features.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "auth": true,
3
3
  "cms": true,
4
4
  "type": "landing_page",
5
- "theme": "light_sidebar",
5
+ "theme": "light_glass",
6
6
  "storage": "cloudinary",
7
- "rich_text": true,
7
+ "rich_text": false,
8
8
  "maintenance": false
9
9
  }
data/db/data.sqlite3 CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: one-for-all-framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ishikawa Uta