one-for-all-framework 2.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.
- checksums.yaml +4 -4
- data/README.md +68 -30
- data/app/controllers/cart_controller.rb +52 -0
- data/app/controllers/dashboard_controller.rb +2 -1
- data/app/controllers/products_controller.rb +58 -0
- data/app/models/product.rb +8 -0
- data/app/views/cart_index.erb +112 -0
- data/app/views/cms/pages_form.erb +1 -1
- data/app/views/cms/pages_index.erb +61 -45
- data/app/views/cms/posts_form.erb +1 -1
- data/app/views/cms/posts_index.erb +54 -41
- data/app/views/cms/products_form.erb +89 -0
- data/app/views/cms/products_index.erb +129 -0
- data/app/views/cms/projects_form.erb +8 -8
- data/app/views/cms/projects_index.erb +47 -37
- data/app/views/dashboard.erb +18 -0
- data/app/views/docs.erb +165 -52
- data/app/views/index.erb +1 -1
- data/app/views/layout.erb +26 -7
- data/app/views/product_show.erb +42 -0
- data/app/views/products_index.erb +40 -0
- data/bin/ofa +12 -3
- data/config/boot.rb +1 -0
- data/config/routes.rb +32 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrations/20260502000000_create_products.rb +17 -0
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26eab5d2f3279aef8aaf3e4f4f69af60d01c535a8a4ba2f41a82615f77e02732
|
|
4
|
+
data.tar.gz: a6760ad95277f885fff52a45427049cb87f3a59e584dc09fffa533c4b7b57bd5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac089f1a856bf5b31ffea2561db5a48d2fed567b14be86245c7962965595d9fb80209472d2427a4c10c4f15d17ed48c839897bb3141d0a46f860f43d7014d44b
|
|
7
|
+
data.tar.gz: 9f10a41d2e81293e8cfd363253d08fbb1bd1dc595e97cc646b31ce2c36e30807ee385772c9ba903e98e70bec3205e5b265f0a694cdc3d62aaba12e488639eed0
|
data/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
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
|
|
5
|
+
# ⚡ One-For-All (OFA) Framework v3.0.0
|
|
6
6
|
|
|
7
7
|
[](https://www.ruby-lang.org/)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[]()
|
|
10
10
|
|
|
11
|
-
**One-For-All (OFA)** is a premium, ultra-fast Ruby MVC framework designed for developers who value both high performance and modern aesthetics. Built on the powerful **Eks-Cent** engine and optimized with **Eksa Server**, OFA
|
|
11
|
+
**One-For-All (OFA)** is a premium, ultra-fast Ruby MVC framework designed for developers who value both high performance and modern aesthetics. Built on the powerful **Eks-Cent** engine and optimized with **Eksa Server**, OFA v3.0 now supports **Full E-Commerce integration** alongside its stunning "Glassmorphism" UI.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -52,40 +52,50 @@ Your app is now live at `http://localhost:3000` ⚡
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
## 🛠️ CLI Power Tools
|
|
55
|
+
## 🛠️ CLI Power Tools (Detailed Reference)
|
|
56
56
|
|
|
57
|
-
The `ofa` CLI is
|
|
57
|
+
The `ofa` CLI is the heart of the One-For-All framework. It handles everything from project initialization to production deployment.
|
|
58
58
|
|
|
59
|
-
### Project
|
|
59
|
+
### 📁 Project Lifecycle
|
|
60
60
|
| Command | Description |
|
|
61
61
|
| :--- | :--- |
|
|
62
|
-
| `ofa new NAME [TYPE]` | Create a new project
|
|
63
|
-
| `ofa init [TYPE]` | Initialize
|
|
64
|
-
| `ofa run` | Start the high-performance
|
|
65
|
-
| `ofa deploy` |
|
|
62
|
+
| `ofa new NAME [TYPE]` | **Create a new project.** Generates a new directory, initializes the framework structure, and automatically runs `bundle install`. <br> *Example:* `./ofa new my_portfolio portfolio` |
|
|
63
|
+
| `ofa init [TYPE]` | **Initialize in current folder.** Ideal if you've already created a folder or cloned a repository. It triggers an **Interactive Wizard** to configure your Database (SQLite/MongoDB) and Image Storage (Local/Cloudinary). |
|
|
64
|
+
| `ofa run` | **Start Development Server.** Boots the high-performance Eksa Server. Your app will be accessible at `http://localhost:3000`. |
|
|
65
|
+
| `ofa deploy` | **Production Deployment.** Automatically detects deployment targets. <br> 1. Checks if it's a Git repository. <br> 2. Detects **Railway CLI** and triggers `railway up`. <br> 3. Supports Docker via the included `Dockerfile`. |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### 🏗️ Scaffolding & Generators (`ofa g`)
|
|
70
|
+
Automate the creation of boilerplate code with the generator command.
|
|
66
71
|
|
|
67
|
-
### Generators (Scaffolding)
|
|
68
72
|
| Command | Description |
|
|
69
73
|
| :--- | :--- |
|
|
70
|
-
| `ofa g controller NAME` |
|
|
71
|
-
| `ofa g model NAME` |
|
|
72
|
-
| `ofa g migration NAME` |
|
|
73
|
-
| `ofa g post TITLE` |
|
|
74
|
+
| `ofa g controller NAME` | Creates a new controller in `app/controllers/{name}_controller.rb` with a default `index` action. |
|
|
75
|
+
| `ofa g model NAME` | Generates a database model in `app/models/{name}.rb` integrated with the Sequel ORM. |
|
|
76
|
+
| `ofa g migration NAME` | Creates a timestamped migration file in `db/migrations/`. Use this to define your schema changes. |
|
|
77
|
+
| `ofa g post TITLE` | Creates a new Markdown/ERB post in `app/views/posts/`. <br> *Args:* `--category`, `--author`, `--image`. <br> *Example:* `./ofa g post "My First Journey" --category Tech --author "John Doe"` |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### 🎨 Configuration & Customization
|
|
82
|
+
Fine-tune your application's behavior and appearance without touching the code.
|
|
74
83
|
|
|
75
|
-
### Configuration & Features
|
|
76
84
|
| Command | Description |
|
|
77
85
|
| :--- | :--- |
|
|
78
|
-
| `ofa type
|
|
79
|
-
| `ofa theme
|
|
80
|
-
| `ofa feature
|
|
81
|
-
| `ofa storage
|
|
82
|
-
|
|
86
|
+
| `ofa type NAME` | **Set Application Type.** Switches the layout logic between `portfolio`, `blog`, `landing_page`, and `e_commerce`. |
|
|
87
|
+
| `ofa theme NAME` | **Change UI Aesthetic.** Instantly swap between premium themes: <br> • `light_glass` / `dark_glass` (Modern Glassmorphism) <br> • `cyber_sidebar` (High-tech) <br> • `retro_terminal` (Old-school hacker vibe) <br> • `light_sidebar` (Professional/Clean) |
|
|
88
|
+
| `ofa feature ACTION FEATURE`| **Toggle Core Features.** Enable or disable system modules. <br> *Usage:* `./ofa feature enable auth` or `./ofa feature disable cms`. |
|
|
89
|
+
| `ofa storage NAME` | **Set Media Storage.** Choose between `local` (uploads folder) or `cloudinary` (Cloud storage). |
|
|
90
|
+
|
|
91
|
+
---
|
|
83
92
|
|
|
84
|
-
### Database
|
|
93
|
+
### 🔐 Security & Database
|
|
85
94
|
| Command | Description |
|
|
86
95
|
| :--- | :--- |
|
|
87
|
-
| `ofa
|
|
88
|
-
| `ofa db
|
|
96
|
+
| `ofa reset-password USR PWD`| **User Management.** Resets a password for an existing admin or creates a new one. <br> *Note:* Enforces strong password rules (8+ chars, 1 uppercase, 1 number). |
|
|
97
|
+
| `ofa db switch ADAPTER` | **Hot-swap Database.** Configure your adapter on the fly: `sqlite`, `mysql`, `mariadb`, `postgres`, or `env` (for MongoDB Atlas). |
|
|
98
|
+
| `ofa db migrate` | **Database Sync.** Runs all pending migrations in `db/migrations/` to keep your schema up to date. |
|
|
89
99
|
|
|
90
100
|
---
|
|
91
101
|
|
|
@@ -100,13 +110,41 @@ OFA follows a strict **MVC (Model-View-Controller)** pattern:
|
|
|
100
110
|
|
|
101
111
|
---
|
|
102
112
|
|
|
103
|
-
## 🚢 Deployment
|
|
104
|
-
|
|
105
|
-
One-For-All is
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
## 🚢 Deployment Guide
|
|
114
|
+
|
|
115
|
+
One-For-All is designed to be cloud-native and "deploy-ready" from day one.
|
|
116
|
+
|
|
117
|
+
### 🚂 Railway (Recommended)
|
|
118
|
+
Railway is the easiest way to get your OFA app live. The framework includes a `Procfile` that Railway detects automatically.
|
|
119
|
+
1. Install [Railway CLI](https://docs.railway.app/guides/cli).
|
|
120
|
+
2. Run `railway login`.
|
|
121
|
+
3. In your project folder, run:
|
|
122
|
+
```bash
|
|
123
|
+
./ofa deploy
|
|
124
|
+
```
|
|
125
|
+
*The CLI will automatically trigger `railway up` and handle the build process.*
|
|
126
|
+
|
|
127
|
+
### 🐳 Docker
|
|
128
|
+
For customized hosting or VPS providers, use the optimized `Dockerfile`.
|
|
129
|
+
1. **Build the image**:
|
|
130
|
+
```bash
|
|
131
|
+
docker build -t my-ofa-app .
|
|
132
|
+
```
|
|
133
|
+
2. **Run the container**:
|
|
134
|
+
```bash
|
|
135
|
+
docker run -p 3000:3000 --env-file .env my-ofa-app
|
|
136
|
+
```
|
|
137
|
+
*Note: Ensure your `.env` contains production-ready database credentials.*
|
|
138
|
+
|
|
139
|
+
### 🖥️ VPS (DigitalOcean, Linode, AWS)
|
|
140
|
+
To run OFA on a raw Linux server:
|
|
141
|
+
1. **Setup**: Clone your repo and run `bundle install --deployment`.
|
|
142
|
+
2. **Database**: Run `./ofa db migrate` to sync your production schema.
|
|
143
|
+
3. **Process Management**: Use [PM2](https://pm2.keymetrics.io/) to keep the server alive:
|
|
144
|
+
```bash
|
|
145
|
+
pm2 start "./ofa run" --name ofa-app
|
|
146
|
+
```
|
|
147
|
+
4. **Reverse Proxy**: We recommend using **Nginx** as a reverse proxy to handle SSL and port 80/443 forwarding to port 3000.
|
|
110
148
|
|
|
111
149
|
---
|
|
112
150
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require_relative 'application_controller'
|
|
2
|
+
|
|
3
|
+
class CartController < ApplicationController
|
|
4
|
+
def index
|
|
5
|
+
@cart_items = session['cart'] || {}
|
|
6
|
+
# Convert string keys to integers for the query
|
|
7
|
+
@products = Product.where(id: @cart_items.keys.map(&:to_i)).all
|
|
8
|
+
@total = @products.sum { |p| p.price * @cart_items[p.id.to_s].to_i }
|
|
9
|
+
render 'cart_index', title: "Your Cart - One-For-All"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add
|
|
13
|
+
product_id = params['product_id']
|
|
14
|
+
qty = params['qty'].to_i
|
|
15
|
+
qty = 1 if qty <= 0
|
|
16
|
+
|
|
17
|
+
cart = session['cart'] || {}
|
|
18
|
+
cart[product_id] = (cart[product_id].to_i + qty)
|
|
19
|
+
session['cart'] = cart
|
|
20
|
+
|
|
21
|
+
redirect_to '/cart'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def remove
|
|
25
|
+
product_id = params['product_id']
|
|
26
|
+
cart = session['cart'] || {}
|
|
27
|
+
cart.delete(product_id)
|
|
28
|
+
session['cart'] = cart
|
|
29
|
+
|
|
30
|
+
redirect_to '/cart'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update
|
|
34
|
+
product_id = params['product_id']
|
|
35
|
+
qty = params['qty'].to_i
|
|
36
|
+
|
|
37
|
+
cart = session['cart'] || {}
|
|
38
|
+
if qty <= 0
|
|
39
|
+
cart.delete(product_id)
|
|
40
|
+
else
|
|
41
|
+
cart[product_id] = qty
|
|
42
|
+
end
|
|
43
|
+
session['cart'] = cart
|
|
44
|
+
|
|
45
|
+
redirect_to '/cart'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def clear
|
|
49
|
+
session['cart'] = {}
|
|
50
|
+
redirect_to '/cart'
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require_relative 'application_controller'
|
|
2
|
+
|
|
3
|
+
class ProductsController < ApplicationController
|
|
4
|
+
def index
|
|
5
|
+
if req.path.start_with?('/dashboard')
|
|
6
|
+
@products = Product.order(Sequel.desc(:created_at)).all
|
|
7
|
+
render 'cms/products_index'
|
|
8
|
+
else
|
|
9
|
+
@products = Product.where(is_active: true).order(Sequel.desc(:created_at)).all
|
|
10
|
+
render 'products_index', title: "Shop - One-For-All"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def new
|
|
15
|
+
@product = Product.new
|
|
16
|
+
render 'cms/products_form'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create
|
|
20
|
+
data = params['product']
|
|
21
|
+
@product = Product.new(data)
|
|
22
|
+
if @product.save
|
|
23
|
+
redirect_to '/dashboard/products'
|
|
24
|
+
else
|
|
25
|
+
render 'cms/products_form'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def edit
|
|
30
|
+
@product = Product[params['id']]
|
|
31
|
+
render 'cms/products_form'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
@product = Product[params['id']]
|
|
36
|
+
if @product.update(params['product'])
|
|
37
|
+
redirect_to '/dashboard/products'
|
|
38
|
+
else
|
|
39
|
+
render 'cms/products_form'
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def destroy
|
|
44
|
+
@product = Product[params['id']]
|
|
45
|
+
@product.destroy
|
|
46
|
+
redirect_to '/dashboard/products'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def show
|
|
50
|
+
@product = Product.find(slug: params['slug'], is_active: true)
|
|
51
|
+
if @product
|
|
52
|
+
render 'product_show', title: "#{@product.name} - One-For-All"
|
|
53
|
+
else
|
|
54
|
+
@res.status = 404
|
|
55
|
+
render 'error', message: "Product not found."
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<div class="text-center mb-16">
|
|
2
|
+
<h1 class="text-5xl font-black mb-4 tracking-tighter">Your Shopping Cart</h1>
|
|
3
|
+
<p class="text-slate-500 text-lg">Review your items before checkout.</p>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="glass-panel overflow-hidden mb-10">
|
|
7
|
+
<!-- Desktop Header (Hidden on Mobile) -->
|
|
8
|
+
<div class="hidden md:grid grid-cols-12 bg-white/5 border-b border-white/5 text-slate-400 font-bold uppercase text-xs tracking-widest px-8 py-4">
|
|
9
|
+
<div class="col-span-5">Product</div>
|
|
10
|
+
<div class="col-span-2">Price</div>
|
|
11
|
+
<div class="col-span-2 text-center">Quantity</div>
|
|
12
|
+
<div class="col-span-2 text-right">Subtotal</div>
|
|
13
|
+
<div class="col-span-1"></div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="divide-y divide-white/5">
|
|
17
|
+
<% @products.each do |product| %>
|
|
18
|
+
<% qty = @cart_items[product.id.to_s].to_i %>
|
|
19
|
+
<div class="grid grid-cols-1 md:grid-cols-12 items-center gap-4 md:gap-0 p-6 md:px-8 md:py-6 hover:bg-white/[0.02] transition-colors relative group">
|
|
20
|
+
<!-- Product Info -->
|
|
21
|
+
<div class="col-span-1 md:col-span-5">
|
|
22
|
+
<div class="flex items-center gap-4">
|
|
23
|
+
<div class="w-16 h-16 md:w-20 md:h-20 rounded-2xl overflow-hidden bg-slate-900/50 flex-shrink-0">
|
|
24
|
+
<img src="<%= product.image_url || '/images/logo.png' %>" class="w-full h-full object-cover">
|
|
25
|
+
</div>
|
|
26
|
+
<div>
|
|
27
|
+
<div class="font-black text-lg md:text-base"><%= product.name %></div>
|
|
28
|
+
<div class="text-xs text-slate-500 uppercase tracking-tighter"><%= product.category %></div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<!-- Price (Label on Mobile) -->
|
|
34
|
+
<div class="col-span-1 md:col-span-2 flex justify-between md:block items-center">
|
|
35
|
+
<span class="md:hidden text-slate-500 text-sm font-bold uppercase tracking-widest">Price</span>
|
|
36
|
+
<span class="font-mono text-slate-400">$<%= product.price %></span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Quantity -->
|
|
40
|
+
<div class="col-span-1 md:col-span-2 flex justify-between md:block items-center">
|
|
41
|
+
<span class="md:hidden text-slate-500 text-sm font-bold uppercase tracking-widest">Quantity</span>
|
|
42
|
+
<div class="flex items-center justify-center gap-2">
|
|
43
|
+
<form action="/cart/update" method="POST" class="inline">
|
|
44
|
+
<%= csrf_tag %>
|
|
45
|
+
<input type="hidden" name="product_id" value="<%= product.id %>">
|
|
46
|
+
<input type="hidden" name="qty" value="<%= qty - 1 %>">
|
|
47
|
+
<button type="submit" class="w-10 h-10 md:w-8 md:h-8 rounded-xl bg-white/5 hover:bg-red-500/10 hover:text-red-500 transition-all flex items-center justify-center">
|
|
48
|
+
<i class="fas fa-minus text-xs"></i>
|
|
49
|
+
</button>
|
|
50
|
+
</form>
|
|
51
|
+
<span class="w-8 text-center font-black text-lg md:text-base"><%= qty %></span>
|
|
52
|
+
<form action="/cart/update" method="POST" class="inline">
|
|
53
|
+
<%= csrf_tag %>
|
|
54
|
+
<input type="hidden" name="product_id" value="<%= product.id %>">
|
|
55
|
+
<input type="hidden" name="qty" value="<%= qty + 1 %>">
|
|
56
|
+
<button type="submit" class="w-10 h-10 md:w-8 md:h-8 rounded-xl bg-white/5 hover:bg-primary/10 hover:text-primary transition-all flex items-center justify-center">
|
|
57
|
+
<i class="fas fa-plus text-xs"></i>
|
|
58
|
+
</button>
|
|
59
|
+
</form>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Subtotal -->
|
|
64
|
+
<div class="col-span-1 md:col-span-2 flex justify-between md:block items-center border-t md:border-none border-white/5 pt-4 md:pt-0">
|
|
65
|
+
<span class="md:hidden text-slate-500 text-sm font-bold uppercase tracking-widest">Subtotal</span>
|
|
66
|
+
<span class="text-xl md:text-lg md:text-right block font-black text-primary">$<%= product.price * qty %></span>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Remove Btn -->
|
|
70
|
+
<div class="absolute top-6 right-6 md:static md:col-span-1 md:text-right">
|
|
71
|
+
<form action="/cart/remove" method="POST">
|
|
72
|
+
<%= csrf_tag %>
|
|
73
|
+
<input type="hidden" name="product_id" value="<%= product.id %>">
|
|
74
|
+
<button type="submit" class="w-10 h-10 md:w-auto flex items-center justify-center md:inline text-slate-400 hover:text-red-500 transition-colors">
|
|
75
|
+
<i class="fas fa-trash-alt"></i>
|
|
76
|
+
</button>
|
|
77
|
+
</form>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<% end %>
|
|
81
|
+
|
|
82
|
+
<% if @products.empty? %>
|
|
83
|
+
<div class="px-8 py-20 text-center">
|
|
84
|
+
<div class="w-20 h-20 bg-slate-900/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
85
|
+
<i class="fas fa-shopping-basket text-4xl text-slate-700"></i>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="text-slate-500 italic text-lg">Your cart is empty.</div>
|
|
88
|
+
<a href="/" class="btn-premium inline-flex mt-6 px-8">Start Shopping</a>
|
|
89
|
+
</div>
|
|
90
|
+
<% end %>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<% unless @products.empty? %>
|
|
95
|
+
<div class="flex flex-col md:flex-row justify-between items-end gap-8">
|
|
96
|
+
<form action="/cart/clear" method="POST">
|
|
97
|
+
<%= csrf_tag %>
|
|
98
|
+
<button type="submit" class="btn-secondary">Clear Cart</button>
|
|
99
|
+
</form>
|
|
100
|
+
|
|
101
|
+
<div class="glass-card w-full md:w-96 !p-8">
|
|
102
|
+
<div class="flex justify-between items-center mb-6">
|
|
103
|
+
<span class="text-slate-500 font-bold">Total Amount</span>
|
|
104
|
+
<span class="text-3xl font-black text-primary">$<%= @total %></span>
|
|
105
|
+
</div>
|
|
106
|
+
<button class="btn-premium w-full !py-5 !text-xl">
|
|
107
|
+
Proceed to Checkout <i class="fas fa-arrow-right ml-2"></i>
|
|
108
|
+
</button>
|
|
109
|
+
<p class="text-[10px] text-center text-slate-600 mt-4 uppercase tracking-widest font-black">Secure Checkout Powered by OFA.SYS</p>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<% end %>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</aside>
|
|
45
45
|
|
|
46
46
|
<!-- Main Content -->
|
|
47
|
-
<div class="flex-1 space-y-6
|
|
47
|
+
<div class="flex-1 space-y-6 pb-20 min-w-0">
|
|
48
48
|
<div class="flex justify-between items-center bg-white/5 p-6 rounded-3xl border border-white/5">
|
|
49
49
|
<div>
|
|
50
50
|
<h2 class="text-2xl font-black tracking-tight"><%= @page.id ? 'Edit' : 'Add' %> Page</h2>
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
<i class="fas fa-palette w-5"></i>
|
|
30
30
|
<span class="font-semibold">Projects</span>
|
|
31
31
|
</a>
|
|
32
|
+
<% elsif FEATURES_CONFIG['type'] == 'e_commerce' %>
|
|
33
|
+
<a href="/dashboard/products" class="flex items-center gap-3 px-4 py-3 rounded-2xl transition-all duration-300 <%= @req.path.start_with?('/dashboard/products') ? 'bg-primary text-white shadow-lg shadow-primary/30' : 'text-slate-500 hover:bg-white/5' %>">
|
|
34
|
+
<i class="fas fa-tag w-5"></i>
|
|
35
|
+
<span class="font-semibold">Products</span>
|
|
36
|
+
</a>
|
|
32
37
|
<% end %>
|
|
33
38
|
|
|
34
39
|
<div class="h-px bg-white/5 my-4"></div>
|
|
@@ -45,60 +50,71 @@
|
|
|
45
50
|
|
|
46
51
|
<!-- Main Content -->
|
|
47
52
|
<div class="flex-1 space-y-6 w-full">
|
|
48
|
-
<div class="flex justify-between items-center bg-white/5 p-6 rounded-3xl border border-white/5">
|
|
53
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 bg-white/5 p-6 rounded-3xl border border-white/5">
|
|
49
54
|
<div>
|
|
50
55
|
<h2 class="text-2xl font-black tracking-tight">Page Management</h2>
|
|
51
56
|
<p class="text-slate-500 text-sm">Manage all static pages of your website.</p>
|
|
52
57
|
</div>
|
|
53
|
-
<a href="/dashboard/pages/new" class="btn-premium">
|
|
58
|
+
<a href="/dashboard/pages/new" class="btn-premium w-full sm:w-auto text-center">
|
|
54
59
|
<i class="fas fa-plus mr-2 text-sm"></i> New Page
|
|
55
60
|
</a>
|
|
56
61
|
</div>
|
|
57
62
|
|
|
58
63
|
<div class="glass-panel overflow-hidden border-none shadow-xl">
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
</
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
64
|
+
<!-- Header (Hidden on Mobile) -->
|
|
65
|
+
<div class="hidden md:grid grid-cols-12 bg-white/5 border-b border-white/5 text-slate-400 font-bold uppercase text-[10px] tracking-[0.2em] px-8 py-4">
|
|
66
|
+
<div class="col-span-5">Page Title</div>
|
|
67
|
+
<div class="col-span-3">Slug</div>
|
|
68
|
+
<div class="col-span-2">Created</div>
|
|
69
|
+
<div class="col-span-2 text-right">Actions</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="divide-y divide-white/5">
|
|
73
|
+
<% if @pages.empty? %>
|
|
74
|
+
<div class="px-8 py-20 text-center">
|
|
75
|
+
<div class="w-16 h-16 bg-white/5 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
76
|
+
<i class="fas fa-file-circle-plus text-2xl text-slate-600"></i>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="text-slate-500 italic">No pages created yet.</div>
|
|
79
|
+
</div>
|
|
80
|
+
<% end %>
|
|
81
|
+
<% @pages.each do |page| %>
|
|
82
|
+
<div class="grid grid-cols-1 md:grid-cols-12 items-center gap-4 md:gap-0 p-6 md:px-8 md:py-5 hover:bg-white/[0.02] transition-colors relative group">
|
|
83
|
+
<!-- Title -->
|
|
84
|
+
<div class="col-span-1 md:col-span-5">
|
|
85
|
+
<div class="flex flex-col">
|
|
86
|
+
<span class="font-black text-slate-700 dark:text-slate-200 group-hover:text-primary transition-colors text-lg md:text-base"><%= page.title %></span>
|
|
87
|
+
<span class="md:hidden text-[10px] text-slate-500 uppercase tracking-widest font-black mt-1">Page Title</span>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Slug -->
|
|
92
|
+
<div class="col-span-1 md:col-span-3 flex flex-col">
|
|
93
|
+
<code class="text-xs bg-primary/10 px-3 py-1.5 rounded-xl text-primary w-fit font-bold font-mono">/<%= page.slug %></code>
|
|
94
|
+
<span class="md:hidden text-[10px] text-slate-500 uppercase tracking-widest font-black mt-2">Route Slug</span>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- Date -->
|
|
98
|
+
<div class="col-span-1 md:col-span-2 flex flex-col">
|
|
99
|
+
<span class="text-sm text-slate-500 font-bold"><%= page.created_at.strftime('%d %b %Y') %></span>
|
|
100
|
+
<span class="md:hidden text-[10px] text-slate-500 uppercase tracking-widest font-black mt-1">Creation Date</span>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<!-- Actions -->
|
|
104
|
+
<div class="col-span-1 md:col-span-2 flex justify-end gap-2 pt-4 md:pt-0 border-t md:border-none border-white/5">
|
|
105
|
+
<a href="/dashboard/pages/<%= page.id %>/edit" class="w-10 h-10 flex items-center justify-center rounded-2xl bg-primary/10 text-primary hover:bg-primary hover:text-white transition-all shadow-lg shadow-primary/10">
|
|
106
|
+
<i class="fas fa-edit text-sm"></i>
|
|
107
|
+
</a>
|
|
108
|
+
<form action="/dashboard/pages/<%= page.id %>/delete" method="POST" onsubmit="return confirm('Delete this page?')" class="inline">
|
|
109
|
+
<%= csrf_tag %>
|
|
110
|
+
<button type="submit" class="w-10 h-10 flex items-center justify-center rounded-2xl bg-red-500/10 text-red-500 hover:bg-red-500 hover:text-white transition-all shadow-lg shadow-red-500/10">
|
|
111
|
+
<i class="fas fa-trash-can text-sm"></i>
|
|
112
|
+
</button>
|
|
113
|
+
</form>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<% end %>
|
|
117
|
+
</div>
|
|
102
118
|
</div>
|
|
103
119
|
</div>
|
|
104
120
|
</div>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</aside>
|
|
45
45
|
|
|
46
46
|
<!-- Main Content -->
|
|
47
|
-
<div class="flex-1 space-y-6
|
|
47
|
+
<div class="flex-1 space-y-6 pb-20 min-w-0">
|
|
48
48
|
<div class="flex justify-between items-center bg-white/5 p-6 rounded-3xl border border-white/5">
|
|
49
49
|
<div>
|
|
50
50
|
<h2 class="text-2xl font-black tracking-tight"><%= @post.id ? 'Edit' : 'Add' %> Article</h2>
|