ostroner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-project.js +57 -0
- package/package.json +23 -0
- package/templates/stock-chain/backend/.env +9 -0
- package/templates/stock-chain/backend/.eslintrc.js +6 -0
- package/templates/stock-chain/backend/.prettierrc +14 -0
- package/templates/stock-chain/backend/README.md +1 -0
- package/templates/stock-chain/backend/package-lock.json +1666 -0
- package/templates/stock-chain/backend/package.json +21 -0
- package/templates/stock-chain/backend/src/app.js +59 -0
- package/templates/stock-chain/backend/src/config/db.js +29 -0
- package/templates/stock-chain/backend/src/controllers/auth.js +31 -0
- package/templates/stock-chain/backend/src/controllers/bookingcontroller.js +65 -0
- package/templates/stock-chain/backend/src/controllers/buscontroller.js +59 -0
- package/templates/stock-chain/backend/src/controllers/deliverycontroller.js +35 -0
- package/templates/stock-chain/backend/src/controllers/schedulecontroller.js +85 -0
- package/templates/stock-chain/backend/src/controllers/shipmentcontroller.js +35 -0
- package/templates/stock-chain/backend/src/controllers/suppliercontroller.js +35 -0
- package/templates/stock-chain/backend/src/controllers/usercontroller.js +59 -0
- package/templates/stock-chain/backend/src/middleware/auth.js +11 -0
- package/templates/stock-chain/backend/src/middleware/errorHandler.js +7 -0
- package/templates/stock-chain/backend/src/models/delivery.js +20 -0
- package/templates/stock-chain/backend/src/models/index.js +6 -0
- package/templates/stock-chain/backend/src/models/shipment.js +20 -0
- package/templates/stock-chain/backend/src/models/supplier.js +17 -0
- package/templates/stock-chain/backend/src/models/user.js +15 -0
- package/templates/stock-chain/backend/src/routes/authroute.js +11 -0
- package/templates/stock-chain/backend/src/routes/bookingroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/busroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/deliveriesroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/scheduleroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/shipmentsroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/suppliersroute.js +18 -0
- package/templates/stock-chain/backend/src/routes/userroute.js +18 -0
- package/templates/stock-chain/frontend/.env +1 -0
- package/templates/stock-chain/frontend/README.md +16 -0
- package/templates/stock-chain/frontend/eslint.config.js +21 -0
- package/templates/stock-chain/frontend/index.html +13 -0
- package/templates/stock-chain/frontend/package-lock.json +3131 -0
- package/templates/stock-chain/frontend/package.json +33 -0
- package/templates/stock-chain/frontend/public/favicon.svg +1 -0
- package/templates/stock-chain/frontend/public/icons.svg +24 -0
- package/templates/stock-chain/frontend/src/App.jsx +55 -0
- package/templates/stock-chain/frontend/src/assets/hero.png +0 -0
- package/templates/stock-chain/frontend/src/assets/react.svg +1 -0
- package/templates/stock-chain/frontend/src/assets/vite.svg +1 -0
- package/templates/stock-chain/frontend/src/components/Button.jsx +15 -0
- package/templates/stock-chain/frontend/src/components/Input.jsx +25 -0
- package/templates/stock-chain/frontend/src/context/AuthContext.jsx +59 -0
- package/templates/stock-chain/frontend/src/index.css +7 -0
- package/templates/stock-chain/frontend/src/main.jsx +18 -0
- package/templates/stock-chain/frontend/src/pages/AppLayout.jsx +125 -0
- package/templates/stock-chain/frontend/src/pages/Login.jsx +78 -0
- package/templates/stock-chain/frontend/src/pages/ManagerDeliveries.jsx +113 -0
- package/templates/stock-chain/frontend/src/pages/ManagerShipments.jsx +113 -0
- package/templates/stock-chain/frontend/src/pages/ManagerSuppliers.jsx +122 -0
- package/templates/stock-chain/frontend/src/pages/Register.jsx +60 -0
- package/templates/stock-chain/frontend/src/services/api.js +8 -0
- package/templates/stock-chain/frontend/src/services/authService.js +11 -0
- package/templates/stock-chain/frontend/vite.config.js +8 -0
- package/templates/y-bus/backend/.env +9 -0
- package/templates/y-bus/backend/.eslintrc.js +6 -0
- package/templates/y-bus/backend/.prettierrc +14 -0
- package/templates/y-bus/backend/README.md +1 -0
- package/templates/y-bus/backend/package-lock.json +1666 -0
- package/templates/y-bus/backend/package.json +21 -0
- package/templates/y-bus/backend/src/app.js +44 -0
- package/templates/y-bus/backend/src/config/db.js +29 -0
- package/templates/y-bus/backend/src/controllers/auth.js +23 -0
- package/templates/y-bus/backend/src/controllers/bookingcontroller.js +65 -0
- package/templates/y-bus/backend/src/controllers/buscontroller.js +59 -0
- package/templates/y-bus/backend/src/controllers/schedulecontroller.js +85 -0
- package/templates/y-bus/backend/src/controllers/usercontroller.js +59 -0
- package/templates/y-bus/backend/src/middleware/auth.js +11 -0
- package/templates/y-bus/backend/src/middleware/errorHandler.js +7 -0
- package/templates/y-bus/backend/src/models/booking.js +27 -0
- package/templates/y-bus/backend/src/models/bus.js +16 -0
- package/templates/y-bus/backend/src/models/index.js +15 -0
- package/templates/y-bus/backend/src/models/schedule.js +25 -0
- package/templates/y-bus/backend/src/models/user.js +20 -0
- package/templates/y-bus/backend/src/routes/bookingroute.js +18 -0
- package/templates/y-bus/backend/src/routes/busroute.js +18 -0
- package/templates/y-bus/backend/src/routes/scheduleroute.js +18 -0
- package/templates/y-bus/backend/src/routes/userroute.js +18 -0
- package/templates/y-bus/frontend/.env +1 -0
- package/templates/y-bus/frontend/README.md +16 -0
- package/templates/y-bus/frontend/eslint.config.js +21 -0
- package/templates/y-bus/frontend/index.html +13 -0
- package/templates/y-bus/frontend/package-lock.json +3131 -0
- package/templates/y-bus/frontend/package.json +33 -0
- package/templates/y-bus/frontend/public/favicon.svg +1 -0
- package/templates/y-bus/frontend/public/icons.svg +24 -0
- package/templates/y-bus/frontend/src/App.jsx +108 -0
- package/templates/y-bus/frontend/src/assets/hero.png +0 -0
- package/templates/y-bus/frontend/src/assets/react.svg +1 -0
- package/templates/y-bus/frontend/src/assets/vite.svg +1 -0
- package/templates/y-bus/frontend/src/components/Button.jsx +15 -0
- package/templates/y-bus/frontend/src/components/Input.jsx +25 -0
- package/templates/y-bus/frontend/src/context/AuthContext.jsx +59 -0
- package/templates/y-bus/frontend/src/index.css +7 -0
- package/templates/y-bus/frontend/src/main.jsx +18 -0
- package/templates/y-bus/frontend/src/pages/AppLayout.jsx +135 -0
- package/templates/y-bus/frontend/src/pages/CustomerTrips.jsx +101 -0
- package/templates/y-bus/frontend/src/pages/Login.jsx +81 -0
- package/templates/y-bus/frontend/src/pages/ManagerBuses.jsx +140 -0
- package/templates/y-bus/frontend/src/pages/ManagerDashboard.jsx +108 -0
- package/templates/y-bus/frontend/src/pages/ManagerReport.jsx +89 -0
- package/templates/y-bus/frontend/src/pages/ManagerSchedules.jsx +233 -0
- package/templates/y-bus/frontend/src/pages/MyBookings.jsx +78 -0
- package/templates/y-bus/frontend/src/pages/Register.jsx +67 -0
- package/templates/y-bus/frontend/src/services/api.js +8 -0
- package/templates/y-bus/frontend/src/services/authService.js +33 -0
- package/templates/y-bus/frontend/vite.config.js +8 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import toast from "react-hot-toast";
|
|
3
|
+
import { Button } from "../components/Button";
|
|
4
|
+
import { Input } from "../components/Input";
|
|
5
|
+
import api from "../services/api";
|
|
6
|
+
|
|
7
|
+
const emptyForm = { shipmentNumber: "", shipmentDate: "", shipmentStatus: "pending", destination: "" };
|
|
8
|
+
|
|
9
|
+
export function ManagerShipments() {
|
|
10
|
+
const [items, setItems] = useState([]);
|
|
11
|
+
const [form, setForm] = useState(emptyForm);
|
|
12
|
+
const [editingId, setEditingId] = useState(null);
|
|
13
|
+
|
|
14
|
+
async function load() {
|
|
15
|
+
const response = await api.get("/shipments");
|
|
16
|
+
setItems(response.data.data || []);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
load().catch(() => toast.error("Failed to load shipments"));
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
async function handleSubmit(event) {
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
try {
|
|
26
|
+
const payload = { ...form };
|
|
27
|
+
if (editingId) await api.put(`/shipments/${editingId}`, payload);
|
|
28
|
+
else await api.post("/shipments", payload);
|
|
29
|
+
|
|
30
|
+
toast.success(editingId ? "Shipment updated" : "Shipment created");
|
|
31
|
+
setForm(emptyForm);
|
|
32
|
+
setEditingId(null);
|
|
33
|
+
await load();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
toast.error(error.response?.data?.message || "Saving shipment failed");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function deleteItem(id) {
|
|
40
|
+
if (!confirm("Delete this shipment?")) return;
|
|
41
|
+
try {
|
|
42
|
+
await api.delete(`/shipments/${id}`);
|
|
43
|
+
toast.success("Shipment deleted");
|
|
44
|
+
await load();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
toast.error(error.response?.data?.message || "Delete failed");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function editItem(item) {
|
|
51
|
+
setEditingId(item.shipmentNumber);
|
|
52
|
+
setForm({ shipmentNumber: item.shipmentNumber, shipmentDate: item.shipmentDate, shipmentStatus: item.shipmentStatus, destination: item.destination });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="grid gap-6 xl:grid-cols-[360px_1fr]">
|
|
57
|
+
<form onSubmit={handleSubmit} className="h-fit rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
|
|
58
|
+
<h1 className="text-2xl font-bold text-gray-950">{editingId ? "Edit shipment" : "Create shipment"}</h1>
|
|
59
|
+
<div className="mt-5 grid gap-4">
|
|
60
|
+
<Input label="Number" value={form.shipmentNumber} onChange={(e) => setForm({ ...form, shipmentNumber: e.target.value })} required />
|
|
61
|
+
<Input label="Date" type="date" value={form.shipmentDate} onChange={(e) => setForm({ ...form, shipmentDate: e.target.value })} required />
|
|
62
|
+
<Input label="Destination" value={form.destination} onChange={(e) => setForm({ ...form, destination: e.target.value })} required />
|
|
63
|
+
<Input label="Status" value={form.shipmentStatus} onChange={(e) => setForm({ ...form, shipmentStatus: e.target.value })} />
|
|
64
|
+
<Button type="submit">{editingId ? "Update shipment" : "Save shipment"}</Button>
|
|
65
|
+
{editingId ? (
|
|
66
|
+
<Button type="button" variant="light" onClick={() => { setEditingId(null); setForm(emptyForm); }}>Cancel edit</Button>
|
|
67
|
+
) : null}
|
|
68
|
+
</div>
|
|
69
|
+
</form>
|
|
70
|
+
|
|
71
|
+
<section className="space-y-4">
|
|
72
|
+
<div>
|
|
73
|
+
<h1 className="text-2xl font-bold text-gray-950">Shipments</h1>
|
|
74
|
+
<p className="mt-1 text-sm text-gray-500">Manage shipment records.</p>
|
|
75
|
+
</div>
|
|
76
|
+
<div className="overflow-x-auto rounded-2xl border border-gray-200 bg-white">
|
|
77
|
+
<table className="min-w-full text-left text-sm">
|
|
78
|
+
<thead className="bg-gray-50 text-xs uppercase tracking-wide text-gray-600">
|
|
79
|
+
<tr>
|
|
80
|
+
<th className="px-4 py-3 font-semibold">Number</th>
|
|
81
|
+
<th className="px-4 py-3 font-semibold">Date</th>
|
|
82
|
+
<th className="px-4 py-3 font-semibold">Destination</th>
|
|
83
|
+
<th className="px-4 py-3 font-semibold">Status</th>
|
|
84
|
+
<th className="px-4 py-3 font-semibold">Actions</th>
|
|
85
|
+
</tr>
|
|
86
|
+
</thead>
|
|
87
|
+
<tbody>
|
|
88
|
+
{items.map((it) => (
|
|
89
|
+
<tr key={it.shipmentNumber}>
|
|
90
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top font-semibold text-gray-950">{it.shipmentNumber}</td>
|
|
91
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{new Date(it.shipmentDate).toLocaleDateString()}</td>
|
|
92
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{it.destination}</td>
|
|
93
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{it.shipmentStatus}</td>
|
|
94
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">
|
|
95
|
+
<div className="flex flex-wrap gap-2">
|
|
96
|
+
<Button variant="light" onClick={() => editItem(it)}>Edit</Button>
|
|
97
|
+
<Button variant="light" onClick={() => deleteItem(it.shipmentNumber)}>Delete</Button>
|
|
98
|
+
</div>
|
|
99
|
+
</td>
|
|
100
|
+
</tr>
|
|
101
|
+
))}
|
|
102
|
+
{items.length === 0 ? (
|
|
103
|
+
<tr>
|
|
104
|
+
<td colSpan="5" className="border-b border-gray-100 px-4 py-3 text-center text-gray-500">No shipments found.</td>
|
|
105
|
+
</tr>
|
|
106
|
+
) : null}
|
|
107
|
+
</tbody>
|
|
108
|
+
</table>
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import toast from "react-hot-toast";
|
|
3
|
+
import { Button } from "../components/Button";
|
|
4
|
+
import { Input } from "../components/Input";
|
|
5
|
+
import api from "../services/api";
|
|
6
|
+
|
|
7
|
+
const emptyForm = { supplierCode: "", supplierName: "", telephone: "", address: "", email: "" };
|
|
8
|
+
|
|
9
|
+
export function ManagerSuppliers() {
|
|
10
|
+
const [items, setItems] = useState([]);
|
|
11
|
+
const [form, setForm] = useState(emptyForm);
|
|
12
|
+
const [editingId, setEditingId] = useState(null);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
|
|
15
|
+
async function load() {
|
|
16
|
+
try {
|
|
17
|
+
const response = await api.get("/suppliers");
|
|
18
|
+
setItems(response.data.data || []);
|
|
19
|
+
setError(null);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error("Load error:", err);
|
|
22
|
+
setError(err.message);
|
|
23
|
+
toast.error("Failed to load suppliers: " + err.message);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
load();
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
async function handleSubmit(event) {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
try {
|
|
34
|
+
const payload = { ...form };
|
|
35
|
+
if (editingId) await api.put(`/suppliers/${editingId}`, payload);
|
|
36
|
+
else await api.post("/suppliers", payload);
|
|
37
|
+
|
|
38
|
+
toast.success(editingId ? "Supplier updated" : "Supplier created");
|
|
39
|
+
setForm(emptyForm);
|
|
40
|
+
setEditingId(null);
|
|
41
|
+
await load();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
toast.error(error.response?.data?.message || "Saving supplier failed");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function deleteItem(id) {
|
|
48
|
+
if (!confirm("Delete this supplier?")) return;
|
|
49
|
+
try {
|
|
50
|
+
await api.delete(`/suppliers/${id}`);
|
|
51
|
+
toast.success("Supplier deleted");
|
|
52
|
+
await load();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
toast.error(error.response?.data?.message || "Delete failed");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function editItem(item) {
|
|
59
|
+
setEditingId(item.supplierCode);
|
|
60
|
+
setForm({ supplierCode: item.supplierCode, supplierName: item.supplierName, telephone: item.telephone, address: item.address, email: item.email });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="grid gap-6 xl:grid-cols-[360px_1fr]">
|
|
65
|
+
<form onSubmit={handleSubmit} className="h-fit rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
|
|
66
|
+
<h1 className="text-2xl font-bold text-gray-950">{editingId ? "Edit supplier" : "Create supplier"}</h1>
|
|
67
|
+
<div className="mt-5 grid gap-4">
|
|
68
|
+
<Input label="Code" value={form.supplierCode} onChange={(e) => setForm({ ...form, supplierCode: e.target.value })} required />
|
|
69
|
+
<Input label="Name" value={form.supplierName} onChange={(e) => setForm({ ...form, supplierName: e.target.value })} required />
|
|
70
|
+
<Input label="Telephone" value={form.telephone} onChange={(e) => setForm({ ...form, telephone: e.target.value })} />
|
|
71
|
+
<Input label="Address" value={form.address} onChange={(e) => setForm({ ...form, address: e.target.value })} />
|
|
72
|
+
<Input label="Email" type="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} />
|
|
73
|
+
<Button type="submit">{editingId ? "Update supplier" : "Save supplier"}</Button>
|
|
74
|
+
{editingId ? (
|
|
75
|
+
<Button type="button" variant="light" onClick={() => { setEditingId(null); setForm(emptyForm); }}>Cancel edit</Button>
|
|
76
|
+
) : null}
|
|
77
|
+
</div>
|
|
78
|
+
</form>
|
|
79
|
+
|
|
80
|
+
<section className="space-y-4">
|
|
81
|
+
<div>
|
|
82
|
+
<h1 className="text-2xl font-bold text-gray-950">Suppliers</h1>
|
|
83
|
+
<p className="mt-1 text-sm text-gray-500">Manage supplier records used in the system.</p>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="overflow-x-auto rounded-2xl border border-gray-200 bg-white">
|
|
86
|
+
<table className="min-w-full text-left text-sm">
|
|
87
|
+
<thead className="bg-gray-50 text-xs uppercase tracking-wide text-gray-600">
|
|
88
|
+
<tr>
|
|
89
|
+
<th className="px-4 py-3 font-semibold">Code</th>
|
|
90
|
+
<th className="px-4 py-3 font-semibold">Name</th>
|
|
91
|
+
<th className="px-4 py-3 font-semibold">Telephone</th>
|
|
92
|
+
<th className="px-4 py-3 font-semibold">Email</th>
|
|
93
|
+
<th className="px-4 py-3 font-semibold">Actions</th>
|
|
94
|
+
</tr>
|
|
95
|
+
</thead>
|
|
96
|
+
<tbody>
|
|
97
|
+
{items.map((it) => (
|
|
98
|
+
<tr key={it.supplierCode}>
|
|
99
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top font-semibold text-gray-950">{it.supplierCode}</td>
|
|
100
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{it.supplierName}</td>
|
|
101
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{it.telephone}</td>
|
|
102
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">{it.email}</td>
|
|
103
|
+
<td className="border-b border-gray-100 px-4 py-3 align-top">
|
|
104
|
+
<div className="flex flex-wrap gap-2">
|
|
105
|
+
<Button variant="light" onClick={() => editItem(it)}>Edit</Button>
|
|
106
|
+
<Button variant="light" onClick={() => deleteItem(it.supplierCode)}>Delete</Button>
|
|
107
|
+
</div>
|
|
108
|
+
</td>
|
|
109
|
+
</tr>
|
|
110
|
+
))}
|
|
111
|
+
{items.length === 0 ? (
|
|
112
|
+
<tr>
|
|
113
|
+
<td colSpan="5" className="border-b border-gray-100 px-4 py-3 text-center text-gray-500">No suppliers found.</td>
|
|
114
|
+
</tr>
|
|
115
|
+
) : null}
|
|
116
|
+
</tbody>
|
|
117
|
+
</table>
|
|
118
|
+
</div>
|
|
119
|
+
</section>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Link, useNavigate } from "react-router-dom";
|
|
3
|
+
import toast from "react-hot-toast";
|
|
4
|
+
import { useAuth } from "../context/AuthContext";
|
|
5
|
+
import { Button } from "../components/Button";
|
|
6
|
+
import { Input, Select } from "../components/Input";
|
|
7
|
+
|
|
8
|
+
export function Register() {
|
|
9
|
+
const [form, setForm] = useState({ username: "", password: "" });
|
|
10
|
+
const { register } = useAuth();
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
|
|
13
|
+
async function handleSubmit(event) {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
try {
|
|
16
|
+
await register(form);
|
|
17
|
+
toast.success("Account created. Please login.");
|
|
18
|
+
navigate("/login");
|
|
19
|
+
} catch (error) {
|
|
20
|
+
toast.error(error.response?.data?.message || "Registration failed");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<main className="grid min-h-screen place-items-center bg-gray-100 p-4">
|
|
26
|
+
<form
|
|
27
|
+
onSubmit={handleSubmit}
|
|
28
|
+
className="w-full max-w-md rounded-2xl border border-gray-200 bg-white p-6 shadow-sm"
|
|
29
|
+
>
|
|
30
|
+
<h1 className="text-2xl font-bold text-gray-950">Create account</h1>
|
|
31
|
+
<p className="mt-1 text-sm text-gray-500">Choose a role for testing the system.</p>
|
|
32
|
+
|
|
33
|
+
<div className="mt-6 grid gap-4">
|
|
34
|
+
<Input
|
|
35
|
+
label="Username"
|
|
36
|
+
value={form.username}
|
|
37
|
+
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
|
38
|
+
required
|
|
39
|
+
/>
|
|
40
|
+
<Input
|
|
41
|
+
label="Password"
|
|
42
|
+
type="password"
|
|
43
|
+
value={form.password}
|
|
44
|
+
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
|
45
|
+
required
|
|
46
|
+
/>
|
|
47
|
+
{/* No role selection — simple signup */}
|
|
48
|
+
<Button type="submit" className="w-full">Create account</Button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<p className="mt-6 text-center text-sm text-gray-500">
|
|
52
|
+
Already have an account?{" "}
|
|
53
|
+
<Link className="font-semibold text-gray-950 underline" to="/login">
|
|
54
|
+
Login
|
|
55
|
+
</Link>
|
|
56
|
+
</p>
|
|
57
|
+
</form>
|
|
58
|
+
</main>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import api from "./api";
|
|
2
|
+
|
|
3
|
+
export const registerUser = async (payload) => {
|
|
4
|
+
const response = await api.post("/auth/register", payload);
|
|
5
|
+
return response.data;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const loginUser = async (payload) => {
|
|
9
|
+
const response = await api.post("/auth/login", payload);
|
|
10
|
+
return response.data;
|
|
11
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"semi": true,
|
|
3
|
+
"singleQuote": true,
|
|
4
|
+
"trailingComma": "es5",
|
|
5
|
+
"printWidth": 100,
|
|
6
|
+
"tabWidth": 2,
|
|
7
|
+
"useTabs": false,
|
|
8
|
+
"arrowParens": "always",
|
|
9
|
+
"bracketSpacing": true,
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"jsxSingleQuote": false,
|
|
12
|
+
"quoteProps": "as-needed",
|
|
13
|
+
"proseWrap": "preserve"
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Backend Project
|