Edit and save order pools and orders
This commit is contained in:
parent
1f94855504
commit
ca3cbb6e54
8 changed files with 398 additions and 92 deletions
|
|
@ -25,6 +25,9 @@ def user_get(username):
|
||||||
def orders_pool_list(user_id):
|
def orders_pool_list(user_id):
|
||||||
return OrdersPool.select().where(OrdersPool.user_id == user_id)
|
return OrdersPool.select().where(OrdersPool.user_id == user_id)
|
||||||
|
|
||||||
|
def orders_pool(user_id, set_id):
|
||||||
|
return OrdersPool.get(OrdersPool.user_id == user_id, OrdersPool.id == set_id)
|
||||||
|
|
||||||
def domsubusers_add(sub, dom):
|
def domsubusers_add(sub, dom):
|
||||||
return DomSubUsers.create(
|
return DomSubUsers.create(
|
||||||
sub=sub,
|
sub=sub,
|
||||||
|
|
|
||||||
83
flask/api.py
83
flask/api.py
|
|
@ -1,6 +1,7 @@
|
||||||
from flask import Blueprint, jsonify, abort
|
from flask import Blueprint, jsonify, abort, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from db.queries import user_get, domsubusers_list, orders_pool_list
|
from db.models import database, Order
|
||||||
|
from db.queries import user_get, domsubusers_list, orders_pool_list, orders_pool
|
||||||
|
|
||||||
api = Blueprint('api', __name__)
|
api = Blueprint('api', __name__)
|
||||||
|
|
||||||
|
|
@ -14,19 +15,70 @@ def subs():
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.route('/subs/<username>/orders')
|
@api.route('/subs/<username>/sets')
|
||||||
def sub_orders(username):
|
def sub_order_sets(username):
|
||||||
try:
|
try:
|
||||||
sub = user_get(username)
|
sub = user_get(username)
|
||||||
except:
|
except:
|
||||||
abort(500)
|
abort(403)
|
||||||
return
|
return
|
||||||
|
|
||||||
if sub.telegram_username not in [dsu.sub.telegram_username for dsu in domsubusers_list(current_user.db_user)]:
|
if sub.telegram_username not in [dsu.sub.telegram_username for dsu in domsubusers_list(current_user.db_user)]:
|
||||||
abort(500)
|
abort(403)
|
||||||
return
|
return
|
||||||
|
|
||||||
return jsonify([
|
return jsonify([
|
||||||
|
{
|
||||||
|
'id': op.id,
|
||||||
|
'name': op.name,
|
||||||
|
}
|
||||||
|
for op
|
||||||
|
in orders_pool_list(sub.id)
|
||||||
|
])
|
||||||
|
|
||||||
|
@api.route('/subs/<username>/sets/<set_id>', methods = ['GET', 'POST'])
|
||||||
|
def sub_order_set(username, set_id):
|
||||||
|
try:
|
||||||
|
sub = user_get(username)
|
||||||
|
except:
|
||||||
|
abort(403)
|
||||||
|
return
|
||||||
|
|
||||||
|
if sub.telegram_username not in [dsu.sub.telegram_username for dsu in domsubusers_list(current_user.db_user)]:
|
||||||
|
abort(403)
|
||||||
|
return
|
||||||
|
|
||||||
|
op = orders_pool(sub.id, set_id)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
with database.atomic() as transaction:
|
||||||
|
try:
|
||||||
|
op.name = request.json['name']
|
||||||
|
op.save()
|
||||||
|
|
||||||
|
for updated_order in request.json['orders']:
|
||||||
|
if isinstance(updated_order['id'], int):
|
||||||
|
order_to_update = op.orders.where(Order.id == updated_order['id']).get()
|
||||||
|
|
||||||
|
if '_delete' in updated_order:
|
||||||
|
order_to_update.delete_instance(recursive=True)
|
||||||
|
else:
|
||||||
|
order_to_update.name = updated_order['name']
|
||||||
|
order_to_update.weight = updated_order['weight']
|
||||||
|
order_to_update.repeat = updated_order['repeat']
|
||||||
|
|
||||||
|
order_to_update.save()
|
||||||
|
else:
|
||||||
|
Order.create(
|
||||||
|
pool=op,
|
||||||
|
name=updated_order['name'],
|
||||||
|
weight=updated_order['weight'],
|
||||||
|
repeat=updated_order['repeat']
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
transaction.rollback()
|
||||||
|
|
||||||
|
return jsonify(
|
||||||
{
|
{
|
||||||
'id': op.id,
|
'id': op.id,
|
||||||
'name': op.name,
|
'name': op.name,
|
||||||
|
|
@ -34,9 +86,20 @@ def sub_orders(username):
|
||||||
'id': order.id,
|
'id': order.id,
|
||||||
'name': order.name,
|
'name': order.name,
|
||||||
'weight': order.weight,
|
'weight': order.weight,
|
||||||
'repeat': order.repeat
|
'repeat': order.repeat,
|
||||||
|
'add_ons': [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'name': 'Leg Shackles',
|
||||||
|
'probability': 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'name': 'Leather Collar',
|
||||||
|
'probability': 0.5
|
||||||
|
}
|
||||||
|
]
|
||||||
} for order in op.orders]
|
} for order in op.orders]
|
||||||
}
|
}
|
||||||
for op
|
)
|
||||||
in orders_pool_list(sub.id)
|
|
||||||
])
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mantine/core": "^8.3.12",
|
"@mantine/core": "^8.3.12",
|
||||||
|
"@mantine/form": "^8.3.13",
|
||||||
|
"@tabler/icons-react": "^3.36.1",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
|
|
|
||||||
226
flask/vite/src/SubOrderSet.tsx
Normal file
226
flask/vite/src/SubOrderSet.tsx
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Paper,
|
||||||
|
Input,
|
||||||
|
Slider,
|
||||||
|
TextInput,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
ActionIcon,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { randomId } from "@mantine/hooks";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import React from "react";
|
||||||
|
import { Params, useLoaderData, useParams, useNavigate } from "react-router";
|
||||||
|
import { IconMinus, IconPlus } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
export const subOrderSetLoader = async ({
|
||||||
|
params: { username, set_id },
|
||||||
|
}: {
|
||||||
|
params: Params<string>;
|
||||||
|
}) =>
|
||||||
|
fetch(`/api/subs/${username}/sets/${set_id}`).then((response) =>
|
||||||
|
response.json()
|
||||||
|
);
|
||||||
|
|
||||||
|
type FormOrderSetOrderAddOn = Omit<OrderSetOrderAddOn, "id"> & {
|
||||||
|
id: number | string;
|
||||||
|
_delete?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FormOrderSetOrder = Omit<OrderSetOrder, "id" | "add_ons"> & {
|
||||||
|
id: number | string;
|
||||||
|
_delete?: boolean;
|
||||||
|
add_ons: FormOrderSetOrderAddOn[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type FormOrderSet = Omit<OrderSet, "orders"> & {
|
||||||
|
orders: FormOrderSetOrder[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SubOrderSet: React.FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { username, set_id } = useParams();
|
||||||
|
const { id, name, orders } = useLoaderData<OrderSet>();
|
||||||
|
|
||||||
|
const form = useForm<FormOrderSet>({
|
||||||
|
mode: "uncontrolled",
|
||||||
|
initialValues: { id, name, orders },
|
||||||
|
validate: {
|
||||||
|
name: (value) => (value.length < 1 ? "Please enter a name" : null),
|
||||||
|
orders: {
|
||||||
|
name: (value) => (value.length < 1 ? "Please enter a name" : null),
|
||||||
|
add_ons: {
|
||||||
|
name: (value) => (value.length < 1 ? "Please enter a name" : null),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSubmit = React.useCallback(
|
||||||
|
form.onSubmit((values) => {
|
||||||
|
fetch(`/api/subs/${username}/sets/${set_id}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(values),
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
navigate(`/dashboard/subs/${username}`);
|
||||||
|
} else {
|
||||||
|
console.error(response.statusText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
[form]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNewOrder = React.useCallback(() => {
|
||||||
|
form.insertListItem("orders", {
|
||||||
|
id: `new_${randomId()}`,
|
||||||
|
name: "",
|
||||||
|
weight: 10,
|
||||||
|
repeat: 0.8,
|
||||||
|
add_ons: [],
|
||||||
|
});
|
||||||
|
}, [form]);
|
||||||
|
|
||||||
|
const handleRemoveOrder = React.useCallback(
|
||||||
|
(idx: number) => {
|
||||||
|
const orderId = form.getValues().orders[idx].id.toString();
|
||||||
|
|
||||||
|
if (orderId.indexOf("new_") === 0) {
|
||||||
|
form.removeListItem("orders", idx);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`orders.${idx}._delete`, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[form]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNewAddOn = React.useCallback(
|
||||||
|
(idx: number) => {
|
||||||
|
form.insertListItem(`orders.${idx}.add_ons`, {
|
||||||
|
id: `new_${randomId()}`,
|
||||||
|
name: "",
|
||||||
|
probability: 0.5,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[form]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemoveAddOn = React.useCallback(
|
||||||
|
(idx: number, add_on_idx: number) => {
|
||||||
|
const addOnId = form
|
||||||
|
.getValues()
|
||||||
|
.orders[idx].add_ons[add_on_idx].id.toString();
|
||||||
|
|
||||||
|
if (addOnId.indexOf("new_") === 0) {
|
||||||
|
form.removeListItem(`orders.${idx}.add_ons`, add_on_idx);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`orders.${idx}.add_ons.${add_on_idx}._delete`, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[form]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<Container pb="xl">
|
||||||
|
<h1>{name}</h1>
|
||||||
|
<>
|
||||||
|
<TextInput {...form.getInputProps("name")} label="Name" />
|
||||||
|
{form.getValues().orders.map(({ id: order_id, _delete }, idx) =>
|
||||||
|
_delete ? null : (
|
||||||
|
<Paper key={order_id} bg="gray.2">
|
||||||
|
<Flex gap="xl" justify="space-between">
|
||||||
|
<TextInput
|
||||||
|
{...form.getInputProps(`orders.${idx}.name`)}
|
||||||
|
label="Name"
|
||||||
|
flex={1}
|
||||||
|
/>
|
||||||
|
<ActionIcon
|
||||||
|
color="red.8"
|
||||||
|
onClick={() => handleRemoveOrder(idx)}
|
||||||
|
>
|
||||||
|
<IconMinus />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
<Input.Wrapper
|
||||||
|
label="Weight"
|
||||||
|
description="Chance this order will be selected"
|
||||||
|
>
|
||||||
|
<Slider
|
||||||
|
{...form.getInputProps(`orders.${idx}.weight`)}
|
||||||
|
domain={[0, 100]}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
<Input.Wrapper
|
||||||
|
label="Repeat"
|
||||||
|
description="Percent chance that this order will repeat the next day"
|
||||||
|
>
|
||||||
|
<Slider
|
||||||
|
{...form.getInputProps(`orders.${idx}.repeat`)}
|
||||||
|
domain={[0, 1]}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
<h4>Add-On</h4>
|
||||||
|
{form
|
||||||
|
.getValues()
|
||||||
|
.orders[idx].add_ons.map(
|
||||||
|
({ id: add_on_id, _delete: _add_on_delete }, add_on_idx) =>
|
||||||
|
_add_on_delete ? null : (
|
||||||
|
<Paper key={add_on_id} p="xs">
|
||||||
|
<Flex gap="xl" justify="space-between">
|
||||||
|
<TextInput
|
||||||
|
{...form.getInputProps(
|
||||||
|
`orders.${idx}.add_ons.${add_on_idx}.name`
|
||||||
|
)}
|
||||||
|
label="Name"
|
||||||
|
/>
|
||||||
|
<ActionIcon
|
||||||
|
color="red.8"
|
||||||
|
onClick={() => handleRemoveAddOn(idx, add_on_idx)}
|
||||||
|
>
|
||||||
|
<IconMinus />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
<Input.Wrapper label="Probability">
|
||||||
|
<Slider
|
||||||
|
{...form.getInputProps(
|
||||||
|
`orders.${idx}.add_ons.${add_on_idx}.probability`
|
||||||
|
)}
|
||||||
|
name="Probability"
|
||||||
|
domain={[0, 1]}
|
||||||
|
step={0.05}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
<Button onClick={() => handleNewAddOn(idx)}>
|
||||||
|
<IconPlus
|
||||||
|
style={{ width: "60%", height: "60%" }}
|
||||||
|
stroke={2}
|
||||||
|
/>
|
||||||
|
New Add On
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
<Button onClick={handleNewOrder}>
|
||||||
|
<IconPlus style={{ width: "60%", height: "60%" }} stroke={2} />
|
||||||
|
New Order
|
||||||
|
</Button>
|
||||||
|
<Divider my="md" />
|
||||||
|
<Flex justify="flex-end">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
</Container>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
42
flask/vite/src/SubOrderSets.tsx
Normal file
42
flask/vite/src/SubOrderSets.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Accordion,
|
||||||
|
TextInput,
|
||||||
|
Divider,
|
||||||
|
Input,
|
||||||
|
Slider,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import React from "react";
|
||||||
|
import { Link, Params, useLoaderData, useParams } from "react-router";
|
||||||
|
|
||||||
|
export const subOrderSetsLoader = async ({
|
||||||
|
params: { username },
|
||||||
|
}: {
|
||||||
|
params: Params<string>;
|
||||||
|
}) => fetch(`/api/subs/${username}/sets`).then((response) => response.json());
|
||||||
|
|
||||||
|
export const SubOrderSets: React.FC = () => {
|
||||||
|
const { username: sub_username } = useParams();
|
||||||
|
const orders = useLoaderData<
|
||||||
|
{
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
orders: Pick<OrderSet, "id" | "name">[];
|
||||||
|
}[]
|
||||||
|
>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<h1>Orders for {sub_username}</h1>
|
||||||
|
<Accordion>
|
||||||
|
{orders
|
||||||
|
? orders.map(({ id, name }) => (
|
||||||
|
<Link key={id} to={`/dashboard/subs/${sub_username}/${id}`}>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Accordion>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Accordion,
|
|
||||||
TextInput,
|
|
||||||
Divider,
|
|
||||||
Input,
|
|
||||||
Slider,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import React from "react";
|
|
||||||
import { Params, useLoaderData, useParams } from "react-router";
|
|
||||||
|
|
||||||
export const subOrdersLoader = async ({
|
|
||||||
params: { username },
|
|
||||||
}: {
|
|
||||||
params: Params<string>;
|
|
||||||
}) => fetch(`/api/subs/${username}/orders`).then((response) => response.json());
|
|
||||||
|
|
||||||
export const SubOrders: React.FC = () => {
|
|
||||||
const { username: sub_username } = useParams();
|
|
||||||
const orders = useLoaderData<
|
|
||||||
{
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
orders: {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
weight: number;
|
|
||||||
repeat: number;
|
|
||||||
}[];
|
|
||||||
}[]
|
|
||||||
>();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<h1>Orders for {sub_username}</h1>
|
|
||||||
<Accordion>
|
|
||||||
{orders
|
|
||||||
? orders.map(({ id, name, orders }) => (
|
|
||||||
<Accordion.Item key={id} value={`${id}`}>
|
|
||||||
<Accordion.Control>{name}</Accordion.Control>
|
|
||||||
<Accordion.Panel>
|
|
||||||
<>
|
|
||||||
<TextInput label="Name" value={name} />
|
|
||||||
{orders.map(
|
|
||||||
({ id: order_id, name: order_name, weight, repeat }) => (
|
|
||||||
<React.Fragment key={order_id}>
|
|
||||||
<Divider my="md" />
|
|
||||||
<TextInput label="Name" value={order_name} />
|
|
||||||
<Input.Wrapper
|
|
||||||
label="Weight"
|
|
||||||
description="Chance this order will be selected"
|
|
||||||
>
|
|
||||||
<Slider domain={[0, 100]} value={weight} />
|
|
||||||
</Input.Wrapper>
|
|
||||||
<Input.Wrapper
|
|
||||||
label="Repeat"
|
|
||||||
description="Percent chance that this order will repeat the next day"
|
|
||||||
>
|
|
||||||
<Slider
|
|
||||||
domain={[0, 1]}
|
|
||||||
step={0.05}
|
|
||||||
value={repeat}
|
|
||||||
/>
|
|
||||||
</Input.Wrapper>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</Accordion.Panel>
|
|
||||||
</Accordion.Item>
|
|
||||||
))
|
|
||||||
: null}
|
|
||||||
</Accordion>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
19
flask/vite/src/index.d.ts
vendored
Normal file
19
flask/vite/src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
type OrderSetOrderAddOn = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
probability: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderSetOrder = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
weight: number;
|
||||||
|
repeat: number;
|
||||||
|
add_ons: OrderSetOrderAddOn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderSet = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
orders: OrderSetOrder[];
|
||||||
|
}
|
||||||
|
|
@ -2,21 +2,43 @@ import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { createBrowserRouter } from "react-router";
|
import { createBrowserRouter } from "react-router";
|
||||||
import { RouterProvider } from "react-router/dom";
|
import { RouterProvider } from "react-router/dom";
|
||||||
import { createTheme, MantineProvider, Input } from "@mantine/core";
|
import {
|
||||||
|
createTheme,
|
||||||
|
MantineProvider,
|
||||||
|
Input,
|
||||||
|
Paper,
|
||||||
|
Slider,
|
||||||
|
} from "@mantine/core";
|
||||||
|
|
||||||
import "@mantine/core/styles.css";
|
import "@mantine/core/styles.css";
|
||||||
|
|
||||||
import { SubsList, subsListLoader } from "./SubsList";
|
import { SubsList, subsListLoader } from "./SubsList";
|
||||||
import { SubOrders, subOrdersLoader } from "./SubOrders";
|
import { SubOrderSets, subOrderSetsLoader } from "./SubOrderSets";
|
||||||
|
import { SubOrderSet, subOrderSetLoader } from "./SubOrderSet";
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
components: {
|
components: {
|
||||||
InputWrapper: Input.Wrapper.extend({
|
InputWrapper: Input.Wrapper.extend({
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
inputWrapperOrder: ["label", "input", "description", "error"],
|
inputWrapperOrder: ["label", "input", "description", "error"],
|
||||||
my: "md",
|
mb: "xs",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
Paper: Paper.extend({
|
||||||
|
defaultProps: {
|
||||||
|
p: "sm",
|
||||||
|
shadow: "xs",
|
||||||
|
mb: "lg",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Slider: Slider.extend({
|
||||||
|
vars: (theme) =>
|
||||||
|
({
|
||||||
|
root: {
|
||||||
|
"--slider-track-bg": theme.colors.gray[4],
|
||||||
|
},
|
||||||
|
} as any),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -27,9 +49,14 @@ const router = createBrowserRouter([
|
||||||
loader: subsListLoader,
|
loader: subsListLoader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "dashboard/subs/:username",
|
path: "dashboard/subs/:username/",
|
||||||
Component: SubOrders,
|
Component: SubOrderSets,
|
||||||
loader: subOrdersLoader,
|
loader: subOrderSetsLoader,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "dashboard/subs/:username/:set_id",
|
||||||
|
Component: SubOrderSet,
|
||||||
|
loader: subOrderSetLoader,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue