Skip to content

Commit

Permalink
feat: products page
Browse files Browse the repository at this point in the history
  • Loading branch information
mfaizudd committed Jun 30, 2023
1 parent 39f5786 commit 1ff13cd
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 3 deletions.
1 change: 0 additions & 1 deletion SmallRetail.WebApi/Controllers/Resources/ProductRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ public class ProductRequest
{
public required string Name { get; set; }
public required decimal Price { get; set; }
public required int Stock { get; set; }
public required string Barcode { get; set; }
}
}
3 changes: 3 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ migrate +args:
run:
dotnet run --project SmallRetail.WebApi

format:
dotnet format

build-docker:
docker build -t mfaizudd/smallretail-api -f SmallRetail.WebApi/Dockerfile .

Expand Down
2 changes: 2 additions & 0 deletions smallretail-solid/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Redirect from './pages/Redirect';
import Dashboard from './pages/Dashboard';
import { MetaProvider, Title } from '@solidjs/meta';
import Shops from './pages/Shops';
import Products from './pages/Products';

const App: Component = () => {
return (
Expand All @@ -15,6 +16,7 @@ const App: Component = () => {
<Route path="/redirect" component={Redirect} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/shops" component={Shops} />
<Route path="/products" component={Products} />
</Routes>
</MetaProvider>
);
Expand Down
1 change: 1 addition & 0 deletions smallretail-solid/src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Layout: Component<Props> = (props) => {
<>
<Button href="/dashboard" color="secondary">Home</Button>
<Button href="/shops" color="secondary">Shops</Button>
<Button href="/products" color="secondary">Products</Button>
</>
)
const [menuActive, setMenuActive] = createSignal(false);
Expand Down
24 changes: 24 additions & 0 deletions smallretail-solid/src/components/NumberInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component } from "solid-js";

interface Props {
value?: number;
onChange?: (v: number) => void;
name?: string;
placeholder?: string;
id?: string;
}

const NumberInput: Component<Props> = (props) => {
return (
<input
class="p-2 rounded-md border dark:bg-slate-900"
type="number"
value={props.value}
placeholder={props.placeholder}
onChange={e => props.onChange?.(+e.currentTarget.value)}
name={props.name}
id={props.id} />
)
}

export default NumberInput;
4 changes: 2 additions & 2 deletions smallretail-solid/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ const Home: Component = () => {
return (
<div class="bg-slate-200 h-screen flex justify-center items-center gap-3">
<div class="w-max bg-white p-10 rounded-md shadow-md gap-4 flex flex-col items-center">
<h1 class="text-3xl mb-8">Welcome to SmallRetail</h1>
<Button class="w-1/2" href="/dashboard">Dashboard</Button>
<h1 class="text-3xl mb-8 text-center">Welcome to SmallRetail</h1>
<Show when={user()} fallback={signInButton}>
<Button class="w-1/2" href="/dashboard">Dashboard</Button>
<Button class="w-1/2" onClick={signOut}>Sign out</Button>
</Show>
</div>
Expand Down
149 changes: 149 additions & 0 deletions smallretail-solid/src/pages/Products.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import Button from "@/components/Button";
import Form from "@/components/Form";
import Layout from "@/components/Layout";
import Loading from "@/components/Loading";
import Modal from "@/components/Modal";
import NumberInput from "@/components/NumberInput";
import TextInput from "@/components/TextInput";
import { getAuthorizedApi } from "@/lib/api";
import ItemsWrapper from "@/types/ItemsWrapper";
import Product from "@/types/Product";
import { Component, For, createResource, createSignal, onMount } from "solid-js";

const Products: Component = () => {
const [show, setShow] = createSignal(false);
const [confirm, setConfirm] = createSignal(false);
const [callback, setCallback] = createSignal<() => void>(() => { });
const [name, setName] = createSignal("");
const [price, setPrice] = createSignal(0);
const [barcode, setBarcode] = createSignal("");
const [editing, setEditing] = createSignal(false);
const [productIdx, setProductIdx] = createSignal<number>();
const fetch = async () => {
const api = await getAuthorizedApi();
const response = await api.get<ItemsWrapper<Product>>("/products")
return response.data.items;
}
const [products, { refetch }] = createResource<Product[]>(fetch);
onMount(async () => {
fetch();
});
const showConfirm = (callback: () => void) => {
const confirmCallback = () => {
callback();
setConfirm(false);
}
setCallback(_ => confirmCallback)
setConfirm(true);
}
const clear = () => {
setName("");
setPrice(0);
setBarcode("");
}
const edit = (i: number) => {
const product = products()?.[i];
setName(product?.name ?? "");
setPrice(product?.price ?? 0);
setBarcode(product?.barcode ?? "");
setProductIdx(i);
setEditing(true);
setShow(true);
}
const submit = async () => {
const api = await getAuthorizedApi();
const data = {
name: name(),
price: price(),
barcode: barcode(),
}
if (!editing()) {
await api.post("/products", data);
} else {
const product = products()?.[productIdx() ?? -1]
if (product) {
await api.put(`/products/${product.id}`, data)
}
}
refetch();
clear();
setShow(false);
setEditing(false);
}
const deleteProduct = (id: number) => {
showConfirm(async () => {
const api = await getAuthorizedApi();
await api.delete(`/products/${id}`);
refetch();
});
}
return (
<Layout>
<div class="p-4 w-full">
<div class="flex flex-row-reverse">
<Button onClick={() => {
clear();
setEditing(false);
setShow(true);
}}>Add new product</Button>
</div>
<div class="w-full overflow-auto">
<table class="table-auto w-full">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Price</th>
<th>Barcode</th>
<th></th>
</tr>
</thead>
<tbody class="overflow-x-scroll">
<For each={products()}>
{(product, i) => (
<tr class="hover:bg-white/10 p-10">
<td class="text-center p-4">{i() + 1}</td>
<td class="p-4"> {product.name} </td>
<td class="text-center"> {product.price} </td>
<td class="text-center"> {product.barcode} </td>
<td class="flex gap-4 justify-center p-4 sm:flex-row">
<Button onClick={() => edit(i())}>Edit</Button>
<Button onClick={() => deleteProduct(product.id)} color="danger">Delete</Button>
</td>
</tr>
)}
</For>
</tbody>
</table>
</div>
</div>
<Modal show={show()} onClose={() => setShow(false)}>
<div class="font-semibold mb-3">Add new product</div>
<Form class="flex flex-col gap-4" onSubmit={submit}>
<TextInput
value={name()}
onChange={v => setName(v)}
placeholder="Name" />
<NumberInput
value={price()}
onChange={v => setPrice(v)}
placeholder="Price" />
<TextInput
value={barcode()}
onChange={v => setBarcode(v)}
placeholder="Barcode" />
<Button type="submit">Submit</Button>
</Form>
</Modal>
<Modal show={confirm()} onClose={() => setConfirm(false)}>
<div class="font-semibold mb-3">Are you sure?</div>
<div class="flex gap-2 flex-row-reverse">
<Button color="danger" onClick={callback()}>Yes</Button>
<Button color="secondary" onClick={() => setConfirm(false)}>No</Button>
</div>
</Modal>
</Layout>
)
}

export default Products;
4 changes: 4 additions & 0 deletions smallretail-solid/src/types/ItemsWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default interface ItemsWrapper<T> {
items: T[];
total: number;
}
10 changes: 10 additions & 0 deletions smallretail-solid/src/types/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Shop from "@/types/Shop";

export default interface Product {
id: number;
name: string;
price: number;
barcode: string;
user_id: string;
shops?: Shop[];
}
3 changes: 3 additions & 0 deletions smallretail-solid/src/types/Shop.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import Product from "@/types/Product";

export default interface Shop {
id: number;
user_id: number;
name: string;
invite_code?: string;
products?: Product[];
}

0 comments on commit 1ff13cd

Please sign in to comment.