Initial commit

This commit is contained in:
jude
2023-10-28 14:50:37 +01:00
commit 8d384c9e50
91 changed files with 73289 additions and 0 deletions

View File

@ -0,0 +1,32 @@
import { Sidebar } from "../Sidebar";
import { QueryClient, QueryClientProvider } from "react-query";
import { Route, Router, Switch } from "wouter";
import { Welcome } from "../Welcome";
import { GuildReminders } from "../GuildReminders";
export function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<>
<Router base={"/dashboard"}>
<div class="columns is-gapless dashboard-frame">
<Sidebar></Sidebar>
<div class="column is-main-content">
<Switch>
<Route
path={"/:guild/reminders"}
component={GuildReminders}
></Route>
<Route>
<Welcome></Welcome>
</Route>
</Switch>
</div>
</div>
</Router>
</>
</QueryClientProvider>
);
}

View File

@ -0,0 +1,7 @@
import { useParams } from "wouter";
export const GuildReminders = () => {
const params = useParams();
return <>{params.guild}</>;
};

View File

@ -0,0 +1,11 @@
export const Brand = () => (
<div class="brand">
<img
src="/static/img/logo_nobg.webp"
alt="Reminder bot logo"
width="52px"
height="52px"
class="dashboard-brand"
></img>
</div>
);

View File

@ -0,0 +1,5 @@
export const DesktopSidebar = ({ children }) => {
return (
<div class="column is-2 is-sidebar-menu dashboard-sidebar is-hidden-touch">{children}</div>
);
};

View File

@ -0,0 +1,28 @@
import { GuildInfo } from "../../api";
import { Link, useLocation } from "wouter";
type Props = {
guild: GuildInfo;
};
export const GuildEntry = ({ guild }: Props) => {
const [loc] = useLocation();
const currentId = loc.match(/^\/(?<id>\d+)/);
return (
<li>
<Link
class={guild.id === currentId.groups.id ? "is-active switch-pane" : "switch-pane"}
data-pane="guild"
data-guild={guild.id}
data-name={guild.name}
href={`/${guild.id}/reminders`}
>
<span class="icon">
<i class="fas fa-map-pin"></i>
</span>{" "}
<span class="guild-name">{guild.name}</span>
</Link>
</li>
);
};

View File

@ -0,0 +1,7 @@
export const MobileSidebar = ({ children }) => {
return (
<div class="dashboard-sidebar mobile-sidebar is-hidden-desktop" id="mobileSidebar">
{children}
</div>
);
};

View File

@ -0,0 +1,11 @@
export const Wave = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 160">
<g transform="scale(1, 0.5)">
<path
fill="#8fb677"
fill-opacity="1"
d="M0,192L60,170.7C120,149,240,107,360,96C480,85,600,107,720,138.7C840,171,960,213,1080,197.3C1200,181,1320,107,1380,69.3L1440,32L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"
></path>
</g>
</svg>
);

View File

@ -0,0 +1,80 @@
import { useQuery } from "react-query";
import { DesktopSidebar } from "./DesktopSidebar";
import { MobileSidebar } from "./MobileSidebar";
import { Brand } from "./Brand";
import { Wave } from "./Wave";
import { GuildEntry } from "./GuildEntry";
import { fetchUserGuilds, GuildInfo } from "../../api";
import { QueryKeys } from "../../consts";
type ContentProps = {
guilds: GuildInfo[];
};
const SidebarContent = ({ guilds }: ContentProps) => {
const guildEntries = guilds.map((guild) => <GuildEntry guild={guild}></GuildEntry>);
return (
<>
<a href="/">
<Brand></Brand>
</a>
<Wave></Wave>
<aside class="menu">
<p class="menu-label">Servers</p>
<ul class="menu-list guildList">{guildEntries}</ul>
<div class="aside-footer">
<p class="menu-label">Options</p>
<ul class="menu-list">
<li>
<a class="show-modal" data-modal="dataManagerModal">
<span class="icon">
<i class="fas fa-exchange"></i>
</span>{" "}
Import/Export
</a>
<a class="show-modal" data-modal="chooseTimezoneModal">
<span class="icon">
<i class="fas fa-map-marked"></i>
</span>{" "}
Timezone
</a>
<a href="/login/discord/logout">
<span class="icon">
<i class="fas fa-sign-out"></i>
</span>{" "}
Log out
</a>
<a href="https://discord.jellywx.com" class="feedback">
<span class="icon">
<i class="fab fa-discord"></i>
</span>{" "}
Give feedback
</a>
</li>
</ul>
</div>
</aside>
</>
);
};
export const Sidebar = () => {
const { status, data } = useQuery({
queryKey: [QueryKeys.USER_GUILDS],
queryFn: fetchUserGuilds,
staleTime: Infinity,
});
let content = <SidebarContent guilds={[]}></SidebarContent>;
if (status === "success") {
content = <SidebarContent guilds={data}></SidebarContent>;
}
return (
<>
<DesktopSidebar>{content}</DesktopSidebar>
<MobileSidebar>{content}</MobileSidebar>
</>
);
};

View File

@ -0,0 +1,99 @@
import { DateTime } from "luxon";
import { useQuery } from "react-query";
import { QueryKeys } from "../../consts";
import { fetchUserInfo } from "../../api";
type DisplayProps = {
timezone: string;
};
const TimezoneDisplay = ({ timezone }: DisplayProps) => {
const now = DateTime.now().setZone(timezone);
const hour = now.hour;
const minute = now.minute;
return (
<>
<strong>
<span class="set-timezone">{timezone}</span>
</strong>{" "}
(
<span class="set-time">
{hour}:{minute}
</span>
)
</>
);
};
const TimezonePicker = () => {
const browserTimezone = DateTime.now().zone.name;
const { isLoading, isError, data } = useQuery({
queryKey: QueryKeys.USER_DATA,
queryFn: fetchUserInfo,
});
return (
<div class="modal" id="chooseTimezoneModal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<label class="modal-card-title" for="urlInput">
Update Timezone{" "}
<a href="/help/timezone">
<span>
<i class="fa fa-question-circle"></i>
</span>
</a>
</label>
<button class="delete close-modal" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p>
Your configured timezone is:{" "}
<TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay>
<br></br>
<br></br>
Your browser timezone is:{" "}
<TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay>
<br></br>
{!isError && (
<>
Your bot timezone is:{" "}
{isLoading ? (
<i className="fas fa-cog fa-spin"></i>
) : (
<TimezoneDisplay
timezone={data.timezone || "UTC"}
></TimezoneDisplay>
)}
</>
)}
</p>
<br></br>
<div class="has-text-centered">
<button class="button is-success close-modal" id="set-browser-timezone">
<span>Use Browser Timezone</span>{" "}
<span class="icon">
<i class="fab fa-firefox-browser"></i>
</span>
</button>
<button class="button is-link close-modal" id="set-bot-timezone">
<span>Use Bot Timezone</span>{" "}
<span class="icon">
<i class="fab fa-discord"></i>
</span>
</button>
<button class="button is-warning close-modal" id="update-bot-timezone">
Set Bot Timezone
</button>
</div>
</section>
</div>
<button class="modal-close is-large close-modal" aria-label="close"></button>
</div>
);
};

View File

@ -0,0 +1,15 @@
export const Welcome = () => (
<section id="welcome">
<div class="has-text-centered">
<p class="title">Welcome!</p>
<p class="subtitle is-hidden-touch">Select an option from the side to get started</p>
<p class="subtitle is-hidden-desktop">
Press the{" "}
<span class="icon">
<i class="fal fa-bars"></i>
</span>{" "}
to get started
</p>
</div>
</section>
);