Abhyudit22 commited on
Commit
fcbdcef
·
1 Parent(s): 1e6441b

feat: landing Page added

Browse files
frontend/src/App.svelte CHANGED
@@ -3,12 +3,13 @@
3
  import {
4
  pingServer, getProduct, createProduct, adjustStock, deleteProduct,
5
  createDraftBill, addItemToBill, updateBillDetails, finalizeBill,
6
- removeItemFromBill, // Controls deleting items from active draft
7
  getAllSales, getDashboardAnalytics, initiateProductReturn, getAllReturns,
8
- deleteSaleBill // Controls deleting finalized bills from the ledger
9
  } from "./lib/api";
10
  import type { Product, Bill } from "./lib/types";
11
 
 
12
  import Login from "./lib/Login.svelte";
13
  import Dashboard from "./lib/Dashboard.svelte";
14
  import Inventory from "./lib/Inventory.svelte";
@@ -18,6 +19,7 @@
18
 
19
  let websiteName = "Stash GO";
20
 
 
21
  let activePage = $state("dashboard");
22
  let backendStatus = $state("Connecting...");
23
  let products = $state<Product[]>([]);
@@ -36,16 +38,47 @@
36
 
37
  let activeBill: Bill | null = $state(null);
38
  let customerName = $state(""), customerPhone = $state(""), customerEmail = $state("");
39
- let saleSku = $state(""); // Bound securely as a string
40
  let saleQty = $state(1), billingDiscount = $state(0), billingTax = $state(0), paymentMethod = $state("Cash");
41
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  onMount(async () => {
43
  const data = await pingServer();
44
  backendStatus = data.status;
 
45
  if (localStorage.getItem("token")) {
46
  isAuthenticated = true;
 
 
47
  await refreshAllData();
 
 
 
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  });
50
 
51
  async function refreshAllData() {
@@ -126,7 +159,6 @@
126
  async function handleCheckoutAndFinalize() {
127
  if (!activeBill) return;
128
 
129
- // Convert the UI % inputs into flat Rupee amounts for the backend
130
  const flatDiscountAmount = activeBill.subtotal * (billingDiscount / 100);
131
  const flatTaxAmount = activeBill.subtotal * (billingTax / 100);
132
 
@@ -168,53 +200,63 @@
168
  function handleLogout() {
169
  localStorage.removeItem("token");
170
  isAuthenticated = false;
171
- location.reload();
 
172
  }
173
  </script>
174
 
175
- <div class="StashGo-Layout">
176
- {#if !isAuthenticated}
177
- <Login onLoginSuccess={() => { isAuthenticated = true; refreshAllData(); }} />
178
- {:else}
179
- <header>
180
- <div class="header-brand">
181
- <h1>{websiteName}</h1>
182
- <span class="status-indicator {backendStatus === 'ok' ? 'online' : 'offline'}">
183
- Backend: {backendStatus}
184
- </span>
185
- </div>
186
- <nav>
187
- <button class:active={activePage === "dashboard"} onclick={() => activePage = "dashboard"}>Dashboard</button>
188
- <button class:active={activePage === "inventory"} onclick={() => activePage = "inventory"}>Inventory</button>
189
- <button class:active={activePage === "counter"} onclick={() => activePage = "counter"}>Billing Counter</button>
190
- <button class:active={activePage === "sales"} onclick={() => activePage = "sales"}>Sales Ledger</button>
191
- <button class:active={activePage === "returns"} onclick={() => activePage = "returns"}>Returns</button>
192
- <button class="btn-logout" onclick={handleLogout}>Logout</button>
193
- </nav>
194
- </header>
195
-
196
- <main>
197
- {#if activePage === "dashboard"}
198
- <Dashboard {backendStatus} analyticsData={salesAnalytics} />
199
-
200
- {:else if activePage === "inventory"}
201
- <Inventory {products} {handleAddProduct} {handleStockChange} {handleRemove} bind:newProduct />
202
-
203
- {:else if activePage === "counter"}
204
- <Bills
205
- {products} {activeBill} {handleCreateBill} {handleAddItemToBill} {handleCheckoutAndFinalize} {handleRemoveItem}
206
- bind:customerName bind:customerPhone bind:customerEmail bind:saleSku bind:saleQty bind:billingDiscount bind:billingTax bind:paymentMethod
207
- />
208
-
209
- {:else if activePage === "sales"}
210
- <Sales {salesHistory} {handleDeleteSale} />
211
-
212
- {:else if activePage === "returns"}
213
- <Returns {returnsHistory} {handleProcessReturn} />
214
- {/if}
215
- </main>
216
- {/if}
217
- </div>
 
 
 
 
 
 
 
 
 
218
 
219
  <style>
220
  .StashGo-Layout { background-color: #121214; min-height: 100vh; min-width: max-content; color: #e1e1e6; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; }
 
3
  import {
4
  pingServer, getProduct, createProduct, adjustStock, deleteProduct,
5
  createDraftBill, addItemToBill, updateBillDetails, finalizeBill,
6
+ removeItemFromBill,
7
  getAllSales, getDashboardAnalytics, initiateProductReturn, getAllReturns,
8
+ deleteSaleBill
9
  } from "./lib/api";
10
  import type { Product, Bill } from "./lib/types";
11
 
12
+ import Landing from "./lib/Landing.svelte";
13
  import Login from "./lib/Login.svelte";
14
  import Dashboard from "./lib/Dashboard.svelte";
15
  import Inventory from "./lib/Inventory.svelte";
 
19
 
20
  let websiteName = "Stash GO";
21
 
22
+ let showLanding = $state(true);
23
  let activePage = $state("dashboard");
24
  let backendStatus = $state("Connecting...");
25
  let products = $state<Product[]>([]);
 
38
 
39
  let activeBill: Bill | null = $state(null);
40
  let customerName = $state(""), customerPhone = $state(""), customerEmail = $state("");
41
+ let saleSku = $state("");
42
  let saleQty = $state(1), billingDiscount = $state(0), billingTax = $state(0), paymentMethod = $state("Cash");
43
 
44
+ // --- NATIVE ROUTING SYSTEM ---
45
+ function navigate(page: string) {
46
+ activePage = page;
47
+ // Updates the browser URL without refreshing the page
48
+ window.history.pushState({ page }, "", `/${page}`);
49
+ }
50
+
51
+ function handleLaunch() {
52
+ showLanding = false;
53
+ window.history.pushState({ page: 'login' }, "", "/login");
54
+ }
55
+
56
  onMount(async () => {
57
  const data = await pingServer();
58
  backendStatus = data.status;
59
+
60
  if (localStorage.getItem("token")) {
61
  isAuthenticated = true;
62
+ showLanding = false;
63
+ navigate("dashboard"); // Set URL to /dashboard automatically
64
  await refreshAllData();
65
+ } else {
66
+ // If no token, make sure URL is at root
67
+ window.history.pushState({}, "", "/");
68
  }
69
+
70
+ // Handle user clicking the "Back" button in their browser
71
+ window.addEventListener('popstate', (event) => {
72
+ if (event.state && event.state.page) {
73
+ if (event.state.page === 'login') {
74
+ showLanding = false;
75
+ } else {
76
+ activePage = event.state.page;
77
+ }
78
+ } else {
79
+ showLanding = true;
80
+ }
81
+ });
82
  });
83
 
84
  async function refreshAllData() {
 
159
  async function handleCheckoutAndFinalize() {
160
  if (!activeBill) return;
161
 
 
162
  const flatDiscountAmount = activeBill.subtotal * (billingDiscount / 100);
163
  const flatTaxAmount = activeBill.subtotal * (billingTax / 100);
164
 
 
200
  function handleLogout() {
201
  localStorage.removeItem("token");
202
  isAuthenticated = false;
203
+ showLanding = true;
204
+ window.history.pushState({}, "", "/"); // Reset URL to root
205
  }
206
  </script>
207
 
208
+ {#if showLanding}
209
+ <Landing launchApp={handleLaunch} />
210
+
211
+ {:else}
212
+ <div class="StashGo-Layout">
213
+ {#if !isAuthenticated}
214
+ <Login onLoginSuccess={() => {
215
+ isAuthenticated = true;
216
+ navigate("dashboard");
217
+ refreshAllData();
218
+ }} />
219
+ {:else}
220
+ <header>
221
+ <div class="header-brand">
222
+ <h1>{websiteName}</h1>
223
+ <span class="status-indicator {backendStatus === 'ok' ? 'online' : 'offline'}">
224
+ Backend: {backendStatus}
225
+ </span>
226
+ </div>
227
+ <nav>
228
+ <button class:active={activePage === "dashboard"} onclick={() => navigate("dashboard")}>Dashboard</button>
229
+ <button class:active={activePage === "inventory"} onclick={() => navigate("inventory")}>Inventory</button>
230
+ <button class:active={activePage === "counter"} onclick={() => navigate("counter")}>Billing Counter</button>
231
+ <button class:active={activePage === "sales"} onclick={() => navigate("sales")}>Sales Ledger</button>
232
+ <button class:active={activePage === "returns"} onclick={() => navigate("returns")}>Returns</button>
233
+ <button class="btn-logout" onclick={handleLogout}>Logout</button>
234
+ </nav>
235
+ </header>
236
+
237
+ <main>
238
+ {#if activePage === "dashboard"}
239
+ <Dashboard {backendStatus} analyticsData={salesAnalytics} />
240
+
241
+ {:else if activePage === "inventory"}
242
+ <Inventory {products} {handleAddProduct} {handleStockChange} {handleRemove} bind:newProduct />
243
+
244
+ {:else if activePage === "counter"}
245
+ <Bills
246
+ {products} {activeBill} {handleCreateBill} {handleAddItemToBill} {handleCheckoutAndFinalize} {handleRemoveItem}
247
+ bind:customerName bind:customerPhone bind:customerEmail bind:saleSku bind:saleQty bind:billingDiscount bind:billingTax bind:paymentMethod
248
+ />
249
+
250
+ {:else if activePage === "sales"}
251
+ <Sales {salesHistory} {handleDeleteSale} />
252
+
253
+ {:else if activePage === "returns"}
254
+ <Returns {returnsHistory} {handleProcessReturn} />
255
+ {/if}
256
+ </main>
257
+ {/if}
258
+ </div>
259
+ {/if}
260
 
261
  <style>
262
  .StashGo-Layout { background-color: #121214; min-height: 100vh; min-width: max-content; color: #e1e1e6; font-family: system-ui, -apple-system, sans-serif; box-sizing: border-box; }
frontend/src/lib/Landing.svelte ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ // This prop will let you switch from the Landing Page to your Dashboard
3
+ export let launchApp: () => void = () => console.log("Routing to app...");
4
+ </script>
5
+
6
+ <div class="landing-container animate-fade-in" id="home">
7
+ <nav class="navbar">
8
+ <div class="logo">
9
+ <span class="logo-icon">📦</span> Stash <span class="text-cyan">GO</span>
10
+ </div>
11
+
12
+ <div class="nav-links">
13
+ <button class="nav-link">Home</button>
14
+ <button class="nav-link" on:click={launchApp}>Login / Sign Up</button>
15
+ <!-- <button class="nav-launch-btn" on:click={launchApp}>Sign Up →</button> -->
16
+ </div>
17
+ </nav>
18
+
19
+ <header class="hero-section">
20
+ <div class="badge">v1.0 is Live</div>
21
+ <h1 class="headline">
22
+ Next-Gen Inventory & POS <br />
23
+ <span class="text-cyan">For Modern Businesses.</span>
24
+ </h1>
25
+ <p class="subtitle">
26
+ Seamlessly manage sales, track inventory, and generate beautiful invoices with a lightning-fast digital terminal built for speed.
27
+ </p>
28
+ <div class="hero-actions">
29
+ <button class="btn-primary" on:click={launchApp}>Launch Terminal</button>
30
+ <a href="https://github.com/Abhyudit22/Stash-Go" target="_blank" class="btn-secondary">View Source Code</a>
31
+ </div>
32
+ </header>
33
+
34
+ <div class="tech-stack-banner">
35
+ <p class="tech-title">ENGINEERED WITH A MODERN STACK</p>
36
+ <div class="tech-logos">
37
+ <span class="tech-badge svelte">Svelte & Vite</span>
38
+ <span class="tech-badge python">FastAPI</span>
39
+ <span class="tech-badge postgres">PostgreSQL</span>
40
+ <span class="tech-badge docker">Docker</span>
41
+ </div>
42
+ </div>
43
+
44
+ <section class="features-grid">
45
+ <div class="feature-card">
46
+ <div class="feature-icon text-cyan">⚡</div>
47
+ <h3>Digital Terminal</h3>
48
+ <p>Lightning-fast checkout system with dynamic tax and discount modifiers. Built for zero latency.</p>
49
+ </div>
50
+
51
+ <div class="feature-card">
52
+ <div class="feature-icon text-green">🧾</div>
53
+ <h3>Invoice Ledger</h3>
54
+ <p>Audit and manage historical sales data securely in a highly organized matrix view.</p>
55
+ </div>
56
+
57
+ <div class="feature-card">
58
+ <div class="feature-icon text-red">📊</div>
59
+ <h3>Live Analytics</h3>
60
+ <p>Real-time custom SVG charts tracking revenue, operational profit margins, and payment distribution.</p>
61
+ </div>
62
+ </section>
63
+
64
+ <footer class="footer">
65
+ <p>Built with ❤️ for the web. © 2026 Stash GO.</p>
66
+ </footer>
67
+ </div>
68
+
69
+ <style>
70
+ .landing-container {
71
+ min-height: 100vh;
72
+ background: #121214;
73
+ color: #e1e1e6;
74
+ font-family: system-ui, -apple-system, sans-serif;
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ }
79
+
80
+ .navbar {
81
+ width: 100%;
82
+ max-width: 1200px;
83
+ display: flex;
84
+ justify-content: space-between;
85
+ align-items: center;
86
+ padding: 20px 40px;
87
+ box-sizing: border-box;
88
+ }
89
+ .logo { font-size: 20px; font-weight: bold; letter-spacing: 1px; display: flex; align-items: center; gap: 8px; }
90
+ .logo-icon { font-size: 24px; }
91
+
92
+
93
+ .nav-links { display: flex; align-items: center; gap: 24px; }
94
+ .nav-link { background: transparent; border: none; color: #a1a1aa; font-size: 15px; font-weight: 500; cursor: pointer; transition: 0.2s; padding: 0; }
95
+ .nav-link:hover { color: #ffffff; }
96
+
97
+ .nav-launch-btn { background: transparent; border: 1px solid #292933; color: #a1a1aa; padding: 8px 16px; border-radius: 6px; cursor: pointer; transition: 0.2s; font-weight: 500; margin-left: 8px; }
98
+ .nav-launch-btn:hover { background: #292933; color: #ffffff; }
99
+
100
+
101
+ .hero-section {
102
+ text-align: center;
103
+ padding: 80px 20px;
104
+ max-width: 800px;
105
+ display: flex;
106
+ flex-direction: column;
107
+ align-items: center;
108
+ }
109
+ .badge { background: rgba(0, 188, 212, 0.1); border: 1px solid rgba(0, 188, 212, 0.2); color: #00bcd4; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; margin-bottom: 24px; letter-spacing: 1px; text-transform: uppercase; }
110
+ .headline { font-size: 56px; font-weight: 800; line-height: 1.1; margin: 0 0 24px 0; color: #ffffff; }
111
+ .subtitle { font-size: 18px; color: #a1a1aa; line-height: 1.6; margin: 0 0 40px 0; max-width: 600px; }
112
+
113
+ .hero-actions { display: flex; gap: 16px; }
114
+ .btn-primary { background: #00bcd4; color: #121214; border: none; padding: 14px 28px; border-radius: 6px; font-size: 15px; font-weight: bold; cursor: pointer; transition: 0.2s; }
115
+ .btn-primary:hover { background: #0097a7; transform: translateY(-2px); }
116
+ .btn-secondary { background: #202028; color: #ffffff; border: 1px solid #292933; padding: 14px 28px; border-radius: 6px; font-size: 15px; font-weight: 600; cursor: pointer; transition: 0.2s; text-decoration: none; }
117
+ .btn-secondary:hover { background: #292933; transform: translateY(-2px); }
118
+
119
+ .tech-stack-banner { border-top: 1px dashed #292933; border-bottom: 1px dashed #292933; width: 100%; padding: 40px 20px; text-align: center; background: #15151a; }
120
+ .tech-title { font-size: 12px; color: #7c7c8a; font-weight: 600; letter-spacing: 2px; margin-bottom: 20px; }
121
+ .tech-logos { display: flex; justify-content: center; gap: 16px; flex-wrap: wrap; }
122
+ .tech-badge { padding: 8px 16px; border-radius: 6px; font-size: 14px; font-weight: 600; background: #1a1a21; border: 1px solid #292933; }
123
+ .svelte { color: #ff3e00; }
124
+ .python { color: #4caf50; }
125
+ .postgres { color: #336791; }
126
+ .docker { color: #0db7ed; }
127
+
128
+ .features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; max-width: 1200px; width: 100%; padding: 80px 40px; box-sizing: border-box; }
129
+ .feature-card { background: #1a1a21; border: 1px solid #292933; padding: 32px; border-radius: 12px; transition: 0.3s; }
130
+ .feature-card:hover { transform: translateY(-5px); border-color: #3a3a47; }
131
+ .feature-icon { font-size: 32px; margin-bottom: 20px; }
132
+ .feature-card h3 { color: #ffffff; font-size: 20px; margin: 0 0 12px 0; }
133
+ .feature-card p { color: #a1a1aa; font-size: 15px; line-height: 1.5; margin: 0; }
134
+
135
+
136
+ .footer { padding: 40px; color: #7c7c8a; font-size: 13px; text-align: center; }
137
+ .text-cyan { color: #00bcd4; }
138
+ .text-green { color: #4caf50; }
139
+ .text-red { color: #ff5252; }
140
+
141
+ .animate-fade-in { animation: fadeIn 0.4s ease-out; }
142
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
143
+ </style>