Dark theme
This commit is contained in:
parent
01589eedeb
commit
4c5c8bec04
12 changed files with 209 additions and 97 deletions
|
|
@ -186,7 +186,7 @@ def timeline_event_put(type, text, user, orders_pool=None, order_status=None, ac
|
|||
extra=json.dumps(extra) if extra is not None else None
|
||||
)
|
||||
|
||||
def timeline_event_recent(user_ids, actor_ids=None):
|
||||
def timeline_event_recent(user_ids, actor_ids=None, limit=5):
|
||||
return (TimelineEvent
|
||||
.select()
|
||||
.where((
|
||||
|
|
@ -194,5 +194,5 @@ def timeline_event_recent(user_ids, actor_ids=None):
|
|||
(TimelineEvent.actor_user_id.in_(actor_ids) if actor_ids is not None else True)
|
||||
))
|
||||
.order_by(TimelineEvent.updated_at.desc())
|
||||
.limit(10)
|
||||
.limit(limit)
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
|
||||
import { Button, Modal, Text, Flex } from "@mantine/core";
|
||||
import { Button, Modal, Text, Flex, ButtonVariant } from "@mantine/core";
|
||||
|
||||
export const ConfirmDialogButton: React.FC<{
|
||||
text: string;
|
||||
|
|
@ -9,7 +9,15 @@ export const ConfirmDialogButton: React.FC<{
|
|||
buttonColor: string;
|
||||
onConfirm: () => void;
|
||||
children: React.ReactNode;
|
||||
}> = ({ text, buttonText, buttonColor, onConfirm, children }) => {
|
||||
variant?: ButtonVariant;
|
||||
}> = ({
|
||||
text,
|
||||
buttonText,
|
||||
buttonColor,
|
||||
onConfirm,
|
||||
children,
|
||||
variant = "filled",
|
||||
}) => {
|
||||
const [opened, { open, close }] = useDisclosure();
|
||||
|
||||
const handleConfirm = React.useCallback(() => {
|
||||
|
|
@ -22,13 +30,15 @@ export const ConfirmDialogButton: React.FC<{
|
|||
<Modal opened={opened} onClose={close} p="sm" withCloseButton={false}>
|
||||
<Text mb="xl">{text}</Text>
|
||||
<Flex gap="md" justify="flex-end">
|
||||
<Button onClick={close}>Cancel</Button>
|
||||
<Button variant="outline" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color={buttonColor} onClick={handleConfirm}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Modal>
|
||||
<Button color={buttonColor} onClick={open}>
|
||||
<Button variant={variant} color={buttonColor} onClick={open}>
|
||||
{children}
|
||||
</Button>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -25,15 +25,7 @@ const SubsList: React.FC<SubsListProps> = ({ subs }) => (
|
|||
</Title>
|
||||
<Flex gap="md" wrap="wrap">
|
||||
{subs.map(({ sub_username, telegram_photo_url }) => (
|
||||
<Card
|
||||
key={sub_username}
|
||||
shadow="sm"
|
||||
padding="lg"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg="gray.2"
|
||||
w="320px"
|
||||
>
|
||||
<Card key={sub_username} padding="lg" withBorder w="320px">
|
||||
{telegram_photo_url ? (
|
||||
<Card.Section>
|
||||
<Image
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import { Avatar, Text, Container, Flex, UnstyledButton } from "@mantine/core";
|
||||
import {
|
||||
Avatar,
|
||||
Text,
|
||||
Container,
|
||||
Flex,
|
||||
UnstyledButton,
|
||||
Anchor,
|
||||
} from "@mantine/core";
|
||||
import React from "react";
|
||||
import { useUserContext } from "./UserContext";
|
||||
import { Outlet, useNavigate } from "react-router";
|
||||
import { Link, Outlet, useNavigate } from "react-router";
|
||||
|
||||
export const Header: React.FC = () => {
|
||||
const { username, telegram_photo_url } = useUserContext();
|
||||
|
|
@ -14,12 +21,12 @@ export const Header: React.FC = () => {
|
|||
return (
|
||||
<Container p="sm">
|
||||
<Flex justify="flex-end">
|
||||
<UnstyledButton onClick={handleClick}>
|
||||
<Anchor component={Link} to={"/profile/"}>
|
||||
<Flex align="center" gap="sm">
|
||||
<Text c="blue">{username}</Text>
|
||||
<Text>{username}</Text>
|
||||
<Avatar src={telegram_photo_url} />
|
||||
</Flex>
|
||||
</UnstyledButton>
|
||||
</Anchor>
|
||||
</Flex>
|
||||
<Outlet />
|
||||
</Container>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
NumberInput,
|
||||
Text,
|
||||
Select,
|
||||
Anchor,
|
||||
} from "@mantine/core";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { TimeInput } from "@mantine/dates";
|
||||
|
|
@ -248,7 +249,9 @@ export const OrderSet: React.FC = () => {
|
|||
<>
|
||||
<Box mb="lg">
|
||||
<Title order={1}>{orderSet?.name || "New Order Set"}</Title>
|
||||
<Link to={`/orders/${username}`}>Return to {username}</Link>
|
||||
<Anchor component={Link} to={`/orders/${username}`}>
|
||||
Return to {username}
|
||||
</Anchor>
|
||||
</Box>
|
||||
<form id="order-set" onSubmit={handleSubmit}>
|
||||
<TextInput {...form.getInputProps("name")} label="Name" />
|
||||
|
|
@ -259,7 +262,7 @@ export const OrderSet: React.FC = () => {
|
|||
mt="lg"
|
||||
/>
|
||||
<Collapse in={showScheduling}>
|
||||
<Paper bg="gray.2">
|
||||
<Paper>
|
||||
<Input.Wrapper
|
||||
error={form.getInputProps("probability").error}
|
||||
label="Probability"
|
||||
|
|
@ -316,7 +319,7 @@ export const OrderSet: React.FC = () => {
|
|||
</Title>
|
||||
{form.getValues().orders.map(({ id: order_id, _delete }, idx) =>
|
||||
_delete ? null : (
|
||||
<Paper key={order_id} bg="gray.2">
|
||||
<Paper key={order_id}>
|
||||
<Flex gap="xl" justify="space-between">
|
||||
<TextInput
|
||||
{...form.getInputProps(`orders.${idx}.name`)}
|
||||
|
|
@ -406,7 +409,7 @@ export const OrderSet: React.FC = () => {
|
|||
</Button>
|
||||
<Box my="40px" />
|
||||
<Affix position={{ bottom: 0 }} w="100%">
|
||||
<Paper mb={0} bg="gray.1">
|
||||
<Paper mb={0}>
|
||||
<Container>
|
||||
<Flex justify="flex-end">
|
||||
<Button type="submit" form="order-set">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
RingProgress,
|
||||
Alert,
|
||||
Grid,
|
||||
Anchor,
|
||||
} from "@mantine/core";
|
||||
import { IconAlertTriangle } from "@tabler/icons-react";
|
||||
import { TimeValue } from "@mantine/dates";
|
||||
|
|
@ -21,18 +22,14 @@ import { useUserContext } from "./UserContext";
|
|||
import { DonutChart } from "@mantine/charts";
|
||||
|
||||
const COLORS_ROTATION = [
|
||||
"teal",
|
||||
"pink",
|
||||
"lime",
|
||||
"violet",
|
||||
"orange",
|
||||
"blue",
|
||||
"yellow",
|
||||
"grape",
|
||||
"green",
|
||||
"red",
|
||||
"cyan",
|
||||
"gray",
|
||||
"orange.8",
|
||||
"gray.7",
|
||||
"orange.7",
|
||||
"gray.6",
|
||||
"orange.6",
|
||||
"gray.5",
|
||||
"orange.5",
|
||||
"gray.4",
|
||||
];
|
||||
|
||||
export interface OrderSetProps {
|
||||
|
|
@ -107,7 +104,9 @@ export const OrderSets: React.FC<OrderSetProps> = ({
|
|||
{username === current_user ? (
|
||||
<>
|
||||
<br />
|
||||
<Link to="/profile/">Edit Profile</Link>
|
||||
<Anchor component={Link} to="/profile/">
|
||||
Edit Profile
|
||||
</Anchor>
|
||||
</>
|
||||
) : null}
|
||||
</Alert>
|
||||
|
|
@ -140,7 +139,6 @@ export const OrderSets: React.FC<OrderSetProps> = ({
|
|||
padding="lg"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg="gray.2"
|
||||
mb="0"
|
||||
>
|
||||
<Flex direction="column" gap="md" h="100%">
|
||||
|
|
@ -163,10 +161,10 @@ export const OrderSets: React.FC<OrderSetProps> = ({
|
|||
</Flex>
|
||||
<Flex gap="md" align="center">
|
||||
{weekdays ? (
|
||||
<Badge color="blue">Weekdays</Badge>
|
||||
<Badge color="orange.7">Weekdays</Badge>
|
||||
) : null}
|
||||
{weekends ? (
|
||||
<Badge color="blue">Weekends</Badge>
|
||||
<Badge color="orange.7">Weekends</Badge>
|
||||
) : null}
|
||||
<RingProgress
|
||||
size={30}
|
||||
|
|
@ -188,7 +186,7 @@ export const OrderSets: React.FC<OrderSetProps> = ({
|
|||
<b>Punishments:</b> {punishment_pool_name}
|
||||
</Text>
|
||||
) : null}
|
||||
<Flex justify="end" align="flex-end" gap="md">
|
||||
<Flex justify="end" align="flex-end" gap="sm">
|
||||
{orders.length > 0 ? (
|
||||
<DonutChart
|
||||
flex={1}
|
||||
|
|
@ -206,6 +204,7 @@ export const OrderSets: React.FC<OrderSetProps> = ({
|
|||
<ConfirmDialogButton
|
||||
buttonColor="red.8"
|
||||
buttonText="Delete"
|
||||
variant="transparent"
|
||||
text={`Are you sure you want to delete ${name}?`}
|
||||
onConfirm={() => handleDelete(id)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { useUserContext } from "./UserContext";
|
||||
import {
|
||||
Anchor,
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
|
|
@ -134,9 +135,11 @@ export const Profile: React.FC = () => {
|
|||
<Title>{username}</Title>
|
||||
</Flex>
|
||||
<Box mb="md">
|
||||
<Link to={`/dashboard/`}>Return to dashboard</Link>
|
||||
<Anchor component={Link} to={`/dashboard/`}>
|
||||
Return to dashboard
|
||||
</Anchor>
|
||||
</Box>
|
||||
<Paper bg="gray.1">
|
||||
<Paper withBorder>
|
||||
<Title order={4}>Mastodon</Title>
|
||||
<TextInput label="Account" w="50%" value={mastodon_account} />
|
||||
<Button onClick={open}>Authorize with Mastodon</Button>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ export const ProfileVerification: React.FC<ProfileVerificationProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<Paper bg="gray.1">
|
||||
<Paper>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Title order={4} mb="md">
|
||||
Order Verification
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { Params, useLoaderData, useParams, Link } from "react-router";
|
||||
import { OrderSetProps, OrderSets } from "./OrderSets";
|
||||
import { ProfileVerification } from "./ProfileVerification";
|
||||
import { Title } from "@mantine/core";
|
||||
import { Anchor, Title } from "@mantine/core";
|
||||
|
||||
export const subOrderSetsLoader = async ({
|
||||
params: { username },
|
||||
|
|
@ -26,7 +26,11 @@ export const SubOrderSets: React.FC = () => {
|
|||
<OrderSets
|
||||
username={sub_username}
|
||||
orderSets={orderSets}
|
||||
linkBack={<Link to={`/dashboard/`}>Return to dashboard</Link>}
|
||||
linkBack={
|
||||
<Anchor component={Link} to={`/dashboard/`}>
|
||||
Return to dashboard
|
||||
</Anchor>
|
||||
}
|
||||
/>
|
||||
{profile ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Timeline, Text, Title, Box, Flex } from "@mantine/core";
|
||||
import { Timeline, Text, Title, Box, Flex, Card, Anchor } from "@mantine/core";
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import { Link } from "react-router";
|
||||
|
|
@ -65,55 +65,60 @@ export const TimelineList: React.FC<{
|
|||
<Title order={1} mb="lg">
|
||||
Timeline
|
||||
</Title>
|
||||
<Timeline bulletSize={24} lineWidth={2} my="lg">
|
||||
{timeline.map(
|
||||
({
|
||||
id,
|
||||
updated_at,
|
||||
type,
|
||||
text,
|
||||
username,
|
||||
actor_username,
|
||||
orders_pool,
|
||||
extra,
|
||||
}) => (
|
||||
<Timeline.Item key={id} active {...(TIMELINE_TYPE[type] ?? {})}>
|
||||
<Text size="sm">
|
||||
{text.split("\n").map((str, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{str}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Text>
|
||||
<Flex mt={4} gap="xs">
|
||||
{extra?.mastodon_status_url ? (
|
||||
<Text size="xs">
|
||||
<a href={extra.mastodon_status_url} target="_blank">
|
||||
Mastodon Post <IconExternalLink size="0.75rem" />
|
||||
</a>
|
||||
</Text>
|
||||
) : null}
|
||||
{orders_pool ? (
|
||||
<Text size="xs">
|
||||
<Link to={`/orders/${username}/${orders_pool.id}`}>
|
||||
{orders_pool.name}
|
||||
</Link>
|
||||
</Text>
|
||||
) : null}
|
||||
{actor_username ? (
|
||||
<Text size="xs">
|
||||
<IconCrown size="0.75rem" /> {actor_username}
|
||||
</Text>
|
||||
) : null}
|
||||
<Text size="xs">
|
||||
<IconLock size="0.75rem" /> {username}
|
||||
<Card py="xs">
|
||||
<Timeline bulletSize={24} lineWidth={2} my="lg">
|
||||
{timeline.map(
|
||||
({
|
||||
id,
|
||||
updated_at,
|
||||
type,
|
||||
text,
|
||||
username,
|
||||
actor_username,
|
||||
orders_pool,
|
||||
extra,
|
||||
}) => (
|
||||
<Timeline.Item key={id} active {...(TIMELINE_TYPE[type] ?? {})}>
|
||||
<Text size="sm">
|
||||
{text.split("\n").map((str, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{str}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Text>
|
||||
<Text size="xs">{moment(updated_at).fromNow()}</Text>
|
||||
</Flex>
|
||||
</Timeline.Item>
|
||||
),
|
||||
)}
|
||||
</Timeline>
|
||||
<Flex mt={4} gap="xs">
|
||||
{extra?.mastodon_status_url ? (
|
||||
<Text size="xs">
|
||||
<Anchor href={extra.mastodon_status_url} target="_blank">
|
||||
Mastodon Post <IconExternalLink size="0.75rem" />
|
||||
</Anchor>
|
||||
</Text>
|
||||
) : null}
|
||||
{orders_pool ? (
|
||||
<Text size="xs">
|
||||
<Anchor
|
||||
component={Link}
|
||||
to={`/orders/${username}/${orders_pool.id}`}
|
||||
>
|
||||
{orders_pool.name}
|
||||
</Anchor>
|
||||
</Text>
|
||||
) : null}
|
||||
{actor_username ? (
|
||||
<Text size="xs">
|
||||
<IconCrown size="0.75rem" /> {actor_username}
|
||||
</Text>
|
||||
) : null}
|
||||
<Text size="xs">
|
||||
<IconLock size="0.75rem" /> {username}
|
||||
</Text>
|
||||
<Text size="xs">{moment(updated_at).fromNow()}</Text>
|
||||
</Flex>
|
||||
</Timeline.Item>
|
||||
),
|
||||
)}
|
||||
</Timeline>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,13 @@ import {
|
|||
Input,
|
||||
Paper,
|
||||
Slider,
|
||||
Image,
|
||||
mergeThemeOverrides,
|
||||
} from "@mantine/core";
|
||||
import { Notifications } from "@mantine/notifications";
|
||||
|
||||
import { mantineTheme } from "./theme";
|
||||
|
||||
import "@mantine/core/styles.css";
|
||||
import "@mantine/dates/styles.css";
|
||||
import "@mantine/notifications/styles.css";
|
||||
|
|
@ -46,6 +50,11 @@ const theme = createTheme({
|
|||
},
|
||||
}) as any,
|
||||
}),
|
||||
Image: Image.extend({
|
||||
defaultProps: {
|
||||
radius: "xs",
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -83,9 +92,11 @@ const router = createBrowserRouter([
|
|||
},
|
||||
]);
|
||||
|
||||
const mergedTheme = mergeThemeOverrides(mantineTheme, theme);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<MantineProvider theme={theme}>
|
||||
<MantineProvider defaultColorScheme="dark" theme={mergedTheme}>
|
||||
<UserContextProvider>
|
||||
<Notifications />
|
||||
<RouterProvider router={router} />
|
||||
|
|
|
|||
78
web/vite/src/theme.ts
Normal file
78
web/vite/src/theme.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { Card, Container, createTheme, Paper, rem, Select } from "@mantine/core";
|
||||
import type { MantineThemeOverride } from "@mantine/core";
|
||||
|
||||
const CONTAINER_SIZES: Record<string, string> = {
|
||||
xxs: rem("200px"),
|
||||
xs: rem("300px"),
|
||||
sm: rem("400px"),
|
||||
md: rem("500px"),
|
||||
lg: rem("600px"),
|
||||
xl: rem("1400px"),
|
||||
xxl: rem("1600px"),
|
||||
};
|
||||
|
||||
export const mantineTheme: MantineThemeOverride = createTheme({
|
||||
/** Put your mantine theme override here */
|
||||
fontSizes: {
|
||||
xs: rem("12px"),
|
||||
sm: rem("14px"),
|
||||
md: rem("16px"),
|
||||
lg: rem("18px"),
|
||||
xl: rem("20px"),
|
||||
"2xl": rem("24px"),
|
||||
"3xl": rem("30px"),
|
||||
"4xl": rem("36px"),
|
||||
"5xl": rem("48px"),
|
||||
},
|
||||
spacing: {
|
||||
"3xs": rem("4px"),
|
||||
"2xs": rem("8px"),
|
||||
xs: rem("10px"),
|
||||
sm: rem("12px"),
|
||||
md: rem("16px"),
|
||||
lg: rem("20px"),
|
||||
xl: rem("24px"),
|
||||
"2xl": rem("28px"),
|
||||
"3xl": rem("32px"),
|
||||
},
|
||||
primaryColor: "orange",
|
||||
components: {
|
||||
/** Put your mantine component override here */
|
||||
Container: Container.extend({
|
||||
vars: (_, { size, fluid }) => ({
|
||||
root: {
|
||||
"--container-size": fluid
|
||||
? "100%"
|
||||
: size !== undefined && size in CONTAINER_SIZES
|
||||
? CONTAINER_SIZES[size]
|
||||
: rem(size),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
Paper: Paper.extend({
|
||||
defaultProps: {
|
||||
p: "md",
|
||||
shadow: "xl",
|
||||
radius: "md",
|
||||
withBorder: true,
|
||||
},
|
||||
}),
|
||||
|
||||
Card: Card.extend({
|
||||
defaultProps: {
|
||||
p: "xl",
|
||||
shadow: "xl",
|
||||
radius: "var(--mantine-radius-default)",
|
||||
withBorder: true,
|
||||
},
|
||||
}),
|
||||
Select: Select.extend({
|
||||
defaultProps: {
|
||||
checkIconPosition: "right",
|
||||
},
|
||||
}),
|
||||
},
|
||||
other: {
|
||||
style: "mantine",
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue