Add dashboard to build

This commit is contained in:
jude
2024-02-24 16:10:25 +00:00
parent 53e13844f9
commit a8ef3d03f9
127 changed files with 75846 additions and 2 deletions

View File

@ -0,0 +1,50 @@
import { ImagePicker } from "../ImagePicker";
import { Reminder } from "../../../api";
type Props = {
name: string;
icon: string;
setReminder: (r: (reminder: Reminder) => Reminder) => void;
};
export const Author = ({ name, icon, setReminder }: Props) => {
return (
<div class="embed-author-box">
<div class="a">
<p class="image is-24x24 customizable">
<ImagePicker
class="is-rounded embed_author_url"
url={icon}
alt="Image for embed author"
setImage={(url) => {
setReminder((reminder) => ({
...reminder,
embed_author_url: url,
}));
}}
></ImagePicker>
</p>
</div>
<div class="b">
<label class="is-sr-only" for="embedAuthor">
Embed Author
</label>
<textarea
class="discord-embed-author message-input autoresize"
placeholder="Embed Author..."
rows={1}
maxlength={256}
name="embed_author"
value={name}
onInput={(ev) => {
setReminder((reminder) => ({
...reminder,
embed_author: ev.currentTarget.value,
}));
}}
></textarea>
</div>
</div>
);
};

View File

@ -0,0 +1,68 @@
import { useState } from "preact/hooks";
import { HexColorPicker } from "react-colorful";
import { Modal } from "../../Modal";
import { Reminder } from "../../../api";
type Props = {
color: string;
setReminder: (r: (reminder: Reminder) => Reminder) => void;
};
function colorToInt(hex: string) {
return parseInt(hex.substring(1), 16);
}
export const Color = ({ color, setReminder }: Props) => {
const [modalOpen, setModalOpen] = useState(false);
return (
<>
{modalOpen && (
<ColorModal
color={color}
setModalOpen={setModalOpen}
setReminder={setReminder}
></ColorModal>
)}
<button
class="change-color button is-rounded is-small"
onClick={() => {
setModalOpen(true);
}}
>
<span class="is-sr-only">Choose embed color</span>
<i class="fas fa-eye-dropper"></i>
</button>
</>
);
};
const ColorModal = ({ setModalOpen, color, setReminder }) => {
return (
<Modal setModalOpen={setModalOpen} title={"Select Color"}>
<div class="colorpicker-container">
<HexColorPicker
color={color}
onInput={(color) => {
setReminder((reminder) => ({
...reminder,
embed_color: colorToInt(color),
}));
}}
></HexColorPicker>
</div>
<br></br>
<input
class="input"
id="colorInput"
value={color}
onInput={(ev) => {
setReminder((reminder) => ({
...reminder,
embed_color: colorToInt(ev.currentTarget.value),
}));
}}
></input>
</Modal>
);
};

View File

@ -0,0 +1,18 @@
export const Description = ({ description, onInput }) => (
<>
<label class="is-sr-only" for="embedDescription">
Embed Description
</label>
<textarea
class="discord-description message-input autoresize "
placeholder="Embed Description..."
maxlength={4096}
name="embed_description"
rows={1}
value={description}
onInput={(ev) => {
onInput(ev.currentTarget.value);
}}
></textarea>
</>
);

View File

@ -0,0 +1,57 @@
export const Field = ({ title, value, inline, index, onUpdate }) => {
return (
<div data-inlined={inline ? "1" : "0"} class="embed-field-box" key={index}>
<label class="is-sr-only" for="embedFieldTitle">
Field Title
</label>
<div class="is-flex">
<textarea
class="discord-field-title field-input message-input autoresize"
placeholder="Field Title..."
rows={1}
maxlength={256}
name="embed_field_title[]"
value={title}
onInput={(ev) =>
onUpdate({
index,
title: ev.currentTarget.value,
})
}
/>
{(value !== "" || title !== "") && (
<button
class="button is-small inline-btn"
onClick={() => {
onUpdate({
index,
inline: !inline,
});
}}
>
<span class="is-sr-only">Toggle field inline</span>
<i class="fas fa-arrows-h"></i>
</button>
)}
</div>
<label class="is-sr-only" for="embedFieldValue">
Field Value
</label>
<textarea
class="discord-field-value field-input message-input autoresize "
placeholder="Field Value..."
maxlength={1024}
name="embed_field_value[]"
rows={1}
value={value}
onInput={(ev) =>
onUpdate({
index,
value: ev.currentTarget.value,
})
}
></textarea>
</div>
);
};

View File

@ -0,0 +1,37 @@
import { useReminder } from "../../ReminderContext";
import { Field } from "./Field";
export const Fields = () => {
const [{ embed_fields }, setReminder] = useReminder();
return (
<div class={"embed-multifield-box"}>
{[...embed_fields, { value: "", title: "", inline: true }].map((field, index) => (
<Field
{...field}
index={index}
onUpdate={({ index, ...props }) => {
setReminder((reminder) => ({
...reminder,
embed_fields: [
...reminder.embed_fields,
{ value: "", title: "", inline: true },
]
.map((f, i) => {
if (i === index) {
return {
...f,
...props,
};
} else {
return f;
}
})
.filter((f) => f.value || f.title),
}));
}}
></Field>
))}
</div>
);
};

View File

@ -0,0 +1,43 @@
import { Reminder } from "../../../api";
import { ImagePicker } from "../ImagePicker";
type Props = {
footer: string;
icon: string;
setReminder: (r: (reminder: Reminder) => Reminder) => void;
};
export const Footer = ({ footer, icon, setReminder }: Props) => (
<div class="embed-footer-box">
<p class="image is-20x20 customizable">
<ImagePicker
class="is-rounded embed_footer_url"
url={icon}
alt="Footer profile-like image"
setImage={(url: string) => {
setReminder((reminder) => ({
...reminder,
embed_footer_url: url,
}));
}}
></ImagePicker>
</p>
<label class="is-sr-only" for="embedFooter">
Embed Footer text
</label>
<textarea
class="discord-embed-footer message-input autoresize "
placeholder="Embed Footer..."
maxlength={2048}
name="embed_footer"
rows={1}
value={footer}
onInput={(ev) => {
setReminder((reminder) => ({
...reminder,
embed_footer: ev.currentTarget.value,
}));
}}
></textarea>
</div>
);

View File

@ -0,0 +1,18 @@
export const Title = ({ title, onInput }) => (
<>
<label class="is-sr-only" for="embedTitle">
Embed Title
</label>
<textarea
class="discord-title message-input autoresize"
placeholder="Embed Title..."
maxlength={256}
rows={1}
name="embed_title"
value={title}
onInput={(ev) => {
onInput(ev.currentTarget.value);
}}
></textarea>
</>
);

View File

@ -0,0 +1,90 @@
import { Author } from "./Author";
import { Title } from "./Title";
import { Description } from "./Description";
import { Footer } from "./Footer";
import { Color } from "./Color";
import { Fields } from "./Fields";
import { Reminder } from "../../../api";
import { ImagePicker } from "../ImagePicker";
import { useReminder } from "../ReminderContext";
function intToColor(num: number) {
return `#${num.toString(16).padStart(6, "0")}`;
}
const DEFAULT_COLOR = 9418359;
export const Embed = () => {
const [reminder, setReminder] = useReminder();
return (
<div
class="discord-embed"
style={{
borderLeftColor: intToColor(reminder.embed_color || DEFAULT_COLOR),
}}
>
<div class="embed-body">
<Color
color={intToColor(reminder.embed_color || DEFAULT_COLOR)}
setReminder={setReminder}
></Color>
<div class="a">
<Author
name={reminder.embed_author}
icon={reminder.embed_author_url}
setReminder={setReminder}
></Author>
<Title
title={reminder.embed_title}
onInput={(title: string) =>
setReminder((reminder: Reminder) => ({
...reminder,
embed_title: title,
}))
}
></Title>
<br></br>
<Description
description={reminder.embed_description}
onInput={(description: string) =>
setReminder((reminder: Reminder) => ({
...reminder,
embed_description: description,
}))
}
/>
<br />
<Fields />
</div>
<div class="b">
<p class="image thumbnail customizable">
<ImagePicker
class="embed_thumbnail_url"
url={reminder.embed_thumbnail_url}
alt="Square thumbnail embedded image"
setImage={() => {}}
/>
</p>
</div>
</div>
<p class="image is-400x300 customizable">
<ImagePicker
class="embed_image_url"
url={reminder.embed_image_url}
alt="Large embedded image"
setImage={() => {}}
/>
</p>
<Footer
footer={reminder.embed_footer}
icon={reminder.embed_footer_url}
setReminder={setReminder}
/>
</div>
);
};