base done

This commit is contained in:
Michael Freno
2025-12-17 01:29:20 -05:00
parent 7479378dd1
commit e8009beacf
39 changed files with 735 additions and 11 deletions

2
.gitignore vendored
View File

@@ -23,7 +23,7 @@ tasks
# Temp
gitignore
*_migration_source
#*_migration_source
# System Files
.DS_Store

View File

@@ -2,17 +2,17 @@ import { Typewriter } from "./Typewriter";
export function LeftBar() {
return (
<nav class="w-fit max-w-[25%] min-h-screen h-full border-r-2 border-r-maroon flex flex-col text-text text-xl font-bold py-10 px-4 gap-4 text-left">
<Typewriter keepAlive={false}>
<nav class="w-fit max-w-[25%] min-h-screen h-full border-r-2 border-r-overlay2 flex flex-col text-text text-xl font-bold py-10 px-4 gap-4 text-left">
<Typewriter keepAlive={false} class="z-50">
<h3 class="text-2xl">Left Navigation</h3>
<ul>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#home">Home</a>
</li>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#about">About</a>
</li>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#services">Services</a>
</li>
</ul>
@@ -23,17 +23,17 @@ export function LeftBar() {
export function RightBar() {
return (
<nav class="w-fit max-w-[25%] min-h-screen h-full border-l-2 border-l-maroon flex flex-col text-text text-xl font-bold py-10 px-4 gap-4 text-right">
<Typewriter keepAlive={false}>
<nav class="w-fit max-w-[25%] min-h-screen h-full border-l-2 border-l-overlay2 flex flex-col text-text text-xl font-bold py-10 px-4 gap-4 text-right">
<Typewriter keepAlive={false} class="z-50">
<h3 class="text-2xl">Right Navigation</h3>
<ul>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#home">Home</a>
</li>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#about">About</a>
</li>
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-green hover:font-bold hover:scale-110">
<li class="hover:-translate-y-0.5 transition-transform duration-200 ease-in-out hover:text-subtext0 hover:font-bold hover:scale-110">
<a href="#services">Services</a>
</li>
</ul>

View File

@@ -0,0 +1,169 @@
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import LoadingSpinner from "~/components/LoadingSpinner";
import { getClientCookie } from "~/lib/cookies.client";
export default function DeletionForm() {
// State management
const [countDown, setCountDown] = createSignal(0);
const [emailSent, setEmailSent] = createSignal(false);
const [error, setError] = createSignal("");
const [loading, setLoading] = createSignal(false);
// Form ref
let emailRef: HTMLInputElement | undefined;
let timerInterval: number | undefined;
// Calculate remaining time from cookie
const calcRemainder = (timer: string) => {
const expires = new Date(timer);
const remaining = expires.getTime() - Date.now();
const remainingInSeconds = remaining / 1000;
if (remainingInSeconds <= 0) {
setCountDown(0);
if (timerInterval) {
clearInterval(timerInterval);
}
} else {
setCountDown(remainingInSeconds);
}
};
// Check for existing timer on mount
createEffect(() => {
const timer = getClientCookie("deletionRequestSent");
if (timer) {
timerInterval = setInterval(() => calcRemainder(timer), 1000) as unknown as number;
onCleanup(() => {
if (timerInterval) {
clearInterval(timerInterval);
}
});
}
});
// Form submission handler
const sendEmailTrigger = async (e: Event) => {
e.preventDefault();
setLoading(true);
setError("");
setEmailSent(false);
if (!emailRef) {
setError("Please enter your email");
setLoading(false);
return;
}
const email = emailRef.value;
try {
const response = await fetch("/api/trpc/misc.sendDeletionRequestEmail", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
const result = await response.json();
if (response.ok && result.result?.data?.message === "request sent") {
setEmailSent(true);
const timer = getClientCookie("deletionRequestSent");
if (timer) {
if (timerInterval) {
clearInterval(timerInterval);
}
timerInterval = setInterval(() => calcRemainder(timer), 1000) as unknown as number;
}
} else {
const errorMsg = result.error?.message || "Failed to send deletion request";
setError(errorMsg);
}
} catch (err: any) {
console.error("Deletion request error:", err);
setError(err.message || "An error occurred");
} finally {
setLoading(false);
}
};
// Countdown timer render function
const renderTime = () => {
return (
<div class="timer">
<div class="value">{countDown().toFixed(0)}</div>
</div>
);
};
return (
<div class="flex min-h-screen w-full justify-center">
<div class="pt-[5vh]">
<div class="text-center text-3xl tracking-widest dark:text-white">
Deletion Form
</div>
<form onSubmit={sendEmailTrigger} class="min-w-[85vw]">
<div class="flex w-full flex-col justify-evenly pt-6 md:mt-24">
<div class="mx-auto w-full justify-evenly md:flex md:w-3/4 md:flex-row lg:w-1/2">
<div class="input-group md:mx-4">
<input
type="email"
required
ref={emailRef}
placeholder=" "
class="underlinedInput w-full bg-transparent"
/>
<span class="bar"></span>
<label class="underlinedInputLabel">Email</label>
</div>
</div>
<div class="mx-auto pt-4">
<Show
when={countDown() > 0}
fallback={
<button
type="submit"
disabled={loading()}
class={`${
loading()
? "bg-zinc-400"
: "bg-red-400 hover:bg-red-500 active:scale-90 dark:bg-red-600 dark:hover:bg-red-700"
} flex w-36 justify-center rounded py-3 font-light text-white shadow-lg shadow-red-300 transition-all duration-300 ease-out dark:shadow-red-700`}
>
<Show when={loading()} fallback="Send Deletion Request">
<LoadingSpinner height={24} width={24} />
</Show>
</button>
}
>
<CountdownCircleTimer
duration={60}
initialRemainingTime={countDown()}
size={48}
strokeWidth={6}
colors="#60a5fa"
>
{renderTime}
</CountdownCircleTimer>
</Show>
</div>
</div>
</form>
<div
class={`${
emailSent()
? "text-green-400"
: error() !== ""
? "text-red-400"
: "select-none opacity-0"
} mt-4 flex justify-center text-center italic transition-opacity duration-300 ease-in-out`}
>
<Show when={emailSent()} fallback={error()}>
Request Sent!
</Show>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,16 @@
export default function LoadingSpinner(props: {
height: number;
width: number;
}) {
return (
<picture class="animate-spin-reverse flex w-full justify-center">
<source srcSet="/WhiteLogo.png" media="(prefers-color-scheme: dark)" />
<img
src="/BlackLogo.png"
alt="logo"
width={props.width}
height={props.height}
/>
</picture>
);
}

View File

@@ -0,0 +1,29 @@
const BackArrow = (props: {
height: number;
width: number;
stroke: string;
strokeWidth: number;
class?: string;
}) => {
return (
<div class={props.class}>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.stroke}
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
/>
</svg>
</div>
);
};
export default BackArrow;

View File

@@ -0,0 +1,25 @@
export default function Check(props: {
strokeWidth: number;
height: number;
width: number;
class?: string;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke="currentColor"
height={props.height}
width={props.width}
class={props.class}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
);
}

View File

@@ -0,0 +1,25 @@
export default function CheckCircle(props: {
strokeWidth: number;
height: number;
width: number;
fillColor: string | null;
strokeColor: string | null;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill={props.fillColor || "none"}
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.strokeColor || "currentColor"}
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
);
}

View File

@@ -0,0 +1,23 @@
export default function CommentIcon(props: {
strokeWidth: number;
height: number;
width: number;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
class="fill-black dark:fill-white"
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 01-.923 1.785A5.969 5.969 0 006 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337z"
/>
</svg>
);
}

View File

@@ -0,0 +1,23 @@
export default function EditIcon(props: {
strokeWidth: number;
height: number;
width: number;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
height={props.height}
width={props.width}
class="stroke-zinc-800 dark:stroke-zinc-50"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
/>
</svg>
);
}

View File

@@ -0,0 +1,25 @@
export default function InfoIcon(props: {
height: number;
width: number;
fill?: string;
stroke?: string;
strokeWidth: number;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill={props.fill ? props.fill : "none"}
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
height={props.height}
width={props.width}
class="stroke-black dark:stroke-white"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
/>
</svg>
);
}

View File

@@ -0,0 +1,24 @@
export default function LikeIcon(props: {
strokeWidth: number;
color: string;
height: number;
width: number;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
height={props.height}
width={props.width}
class={`${props.color} transition-colors duration-200 ease-in`}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z"
/>
</svg>
);
}

View File

@@ -0,0 +1,39 @@
function MenuBars() {
return (
<svg
width="36"
height="30"
viewBox="0 0 120 100"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Mask group">
<g id="Frame 1">
<rect width="120" height="100" />
<line
id="LineA"
x1="11.5"
y1="31.5"
x2="108.5"
y2="31.5"
strokeWidth="6"
strokeLinecap="round"
class="stroke-black dark:stroke-white"
/>
<line
id="LineB"
x1="11.5"
y1="64.5"
x2="108.5"
y2="64.5"
strokeWidth="6"
strokeLinecap="round"
class="stroke-black dark:stroke-white"
/>
</g>
</g>
</svg>
);
}
export default MenuBars;

View File

@@ -0,0 +1,18 @@
export const MoonIcon = (props: any) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={props.size || props.width || 24}
height={props.size || props.height || 24}
viewBox="0 0 24 24"
{...props}
>
<path
fill="#E2E2E2"
d="M20.742,13.045c-0.677,0.18-1.376,0.271-2.077,0.271c-2.135,0-4.14-0.83-5.646-2.336c-2.008-2.008-2.799-4.967-2.064-7.723 c0.092-0.345-0.007-0.713-0.259-0.965C10.444,2.04,10.077,1.938,9.73,2.034C8.028,2.489,6.476,3.382,5.241,4.616 c-3.898,3.898-3.898,10.243,0,14.143c1.889,1.889,4.401,2.93,7.072,2.93c2.671,0,5.182-1.04,7.07-2.929 c1.236-1.237,2.13-2.791,2.583-4.491c0.092-0.345-0.008-0.713-0.26-0.965C21.454,13.051,21.085,12.951,20.742,13.045z M17.97,17.346c-1.511,1.511-3.52,2.343-5.656,2.343c-2.137,0-4.146-0.833-5.658-2.344c-3.118-3.119-3.118-8.195,0-11.314 c0.602-0.602,1.298-1.102,2.06-1.483c-0.222,2.885,0.814,5.772,2.89,7.848c2.068,2.069,4.927,3.12,7.848,2.891 C19.072,16.046,18.571,16.743,17.97,17.346z"
></path>
</svg>
);
};
export default MoonIcon;

View File

@@ -0,0 +1,13 @@
export default function ReplyIcon(props: { color: string; height: number; width: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
fill={props.color}
height={props.height}
width={props.width}
>
<path d="M8.309 189.846L184.31 37.846C199.716 24.549 223.998 35.346 223.998 56.018V136.065C384.624 137.909 512 170.096 512 322.331C512 383.768 472.406 444.643 428.656 476.456C414.999 486.393 395.562 473.924 400.593 457.831C445.937 312.815 379.093 274.315 223.998 272.081V360.003C223.998 380.706 199.685 391.456 184.31 378.159L8.309 226.159C-2.754 216.596 -2.785 199.409 8.309 189.846Z" />
</svg>
);
}

View File

@@ -0,0 +1,38 @@
export const SunIcon = (props: any) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={props.size || props.width || 24}
height={props.size || props.height || 24}
viewBox="0 0 24 24"
{...props}
>
<path
fill={props.fill}
d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"
></path>
<path
fill={props.fill}
transform="rotate(-45.017 5.986 18.01)"
d="M4.487 17.01H7.487V19.01H4.487z"
></path>
<path
fill={props.fill}
transform="rotate(-45.001 18.008 5.99)"
d="M16.508 4.99H19.509V6.99H16.508z"
></path>
<path
fill={props.fill}
transform="rotate(-134.983 5.988 5.99)"
d="M4.487 4.99H7.487V6.99H4.487z"
></path>
<path
fill={props.fill}
transform="rotate(134.999 18.008 18.01)"
d="M17.008 16.51H19.008V19.511000000000003H17.008z"
></path>
</svg>
);
};
export default SunIcon;

View File

@@ -0,0 +1,27 @@
const TrashIcon = (props: {
height: number;
width: number;
stroke?: string;
strokeWidth: number;
class?: string;
}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.stroke ? props.stroke : undefined}
height={props.height}
width={props.width}
class={props.stroke ? undefined : "stroke-zinc-900 dark:stroke-white"}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
/>
</svg>
);
};
export default TrashIcon;

View File

@@ -0,0 +1,25 @@
export default function UpDownArrows(props: {
strokeWidth: number;
height: number;
width: number;
class?: string;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke="currentColor"
height={props.height}
width={props.width}
class={props.class}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3 7.5L7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5"
/>
</svg>
);
}

View File

@@ -0,0 +1,23 @@
export default function UserDefaultImage(props: {
strokeWidth: number;
height: number;
width: number;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
height={props.height}
width={props.width}
class="fill-black dark:fill-white"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
/>
</svg>
);
}

View File

@@ -0,0 +1,25 @@
export default function XCircle(props: {
height: number;
width: number;
stroke: string;
strokeWidth: number;
fill?: string | null;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill={props.fill || "white"}
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.stroke}
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
);
}

View File

@@ -0,0 +1,28 @@
function Xmark(props: {
strokeWidth: number;
color: string;
height: number;
width: number;
}) {
return (
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.color}
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</div>
);
}
export default Xmark;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#e7253e;}.b{fill:#b51f36;}.c{fill:#90192d;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M9.565,8.513A16.121,16.121,0,0,1,5.63,6.328a.52.52,0,0,0-.832.4c-.048,1.583.151,4.483,2.258,4.483A2.6,2.6,0,0,0,9.9,9.1.517.517,0,0,0,9.565,8.513Z"/><path class="c" d="M19.2,6.731a.521.521,0,0,0-.832-.4,16.121,16.121,0,0,1-3.935,2.185A.517.517,0,0,0,14.1,9.1a2.6,2.6,0,0,0,2.84,2.11C19.051,11.214,19.25,8.314,19.2,6.731Z"/><path class="c" d="M14,13.937a.318.318,0,0,1-.313-.326,2.105,2.105,0,0,0-.5-1.331A1.6,1.6,0,0,0,12,11.847a1.6,1.6,0,0,0-1.187.43,2.092,2.092,0,0,0-.5,1.335.32.32,0,0,1-.64.012,2.715,2.715,0,0,1,.679-1.792A2.211,2.211,0,0,1,12,11.207a2.211,2.211,0,0,1,1.647.625,2.721,2.721,0,0,1,.679,1.792A.321.321,0,0,1,14,13.937Z"/></svg>

After

Width:  |  Height:  |  Size: 1020 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,7.313A1.747,1.747,0,1,0,8.8,9.059,1.747,1.747,0,0,0,7.055,7.313Z"/><path class="b" d="M16.958,7.313A1.747,1.747,0,1,0,18.7,9.059,1.747,1.747,0,0,0,16.958,7.313Z"/><path class="c" d="M23,13.938a14.688,14.688,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/></svg>

After

Width:  |  Height:  |  Size: 551 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#e7c930;}.c{fill:#864e20;}.d{fill:#26a9e0;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.688,14.688,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M17.2,12.746c-1.221-1.647-3.789-2.231-5.2-2.231s-3.984.584-5.2,2.231-.188,3.4,1.128,3.293,3.546-.364,4.077-.364,2.762.258,4.077.364S18.427,14.392,17.2,12.746Z"/><path class="b" d="M14.505,17.022A12.492,12.492,0,0,0,12,16.638a12.457,12.457,0,0,0-2.5.384c-.376.076-.39.384,0,.332s2.5-.166,2.5-.166,2.119.115,2.505.166S14.88,17.1,14.505,17.022Z"/><path class="c" d="M8.907,9.844a.182.182,0,0,1-.331.1,2.016,2.016,0,0,0-.569-.567,1.731,1.731,0,0,0-1.915,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,8.907,9.844Z"/><path class="c" d="M18.81,9.844a.182.182,0,0,1-.331.1,2.026,2.026,0,0,0-.568-.567,1.732,1.732,0,0,0-1.916,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,18.81,9.844Z"/><path class="d" d="M8.576,9.946a2.016,2.016,0,0,0-.569-.567,1.731,1.731,0,0,0-1.915,0,2.016,2.016,0,0,0-.571.569.175.175,0,0,1-.214.063v11.24A1.747,1.747,0,0,0,7.054,23h0A1.748,1.748,0,0,0,8.8,21.253V10.005A.176.176,0,0,1,8.576,9.946Z"/><path class="d" d="M18.473,9.946a2.026,2.026,0,0,0-.568-.567,1.732,1.732,0,0,0-1.916,0,2.016,2.016,0,0,0-.571.569.175.175,0,0,1-.214.063v11.24A1.748,1.748,0,0,0,16.952,23h0A1.747,1.747,0,0,0,18.7,21.253V10.005A.176.176,0,0,1,18.473,9.946Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}.d{fill:#fff;}.e{fill:#e6e7e8;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M8.907,9.844a.182.182,0,0,1-.331.1,2.016,2.016,0,0,0-.569-.567,1.731,1.731,0,0,0-1.915,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,8.907,9.844Z"/><path class="b" d="M18.81,9.844a.182.182,0,0,1-.331.1,2.026,2.026,0,0,0-.568-.567,1.732,1.732,0,0,0-1.916,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,18.81,9.844Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="d" d="M7.127,12h9.746a1.937,1.937,0,0,1,1.937,1.937v0a1.938,1.938,0,0,1-1.937,1.938H7.127a1.937,1.937,0,0,1-1.937-1.937v0A1.937,1.937,0,0,1,7.127,12Z"/><ellipse class="e" cx="12" cy="13.938" rx="6.188" ry="0.25"/><ellipse class="e" cx="7.257" cy="13.938" rx="0.208" ry="1.438"/><ellipse class="e" cx="9.628" cy="13.938" rx="0.208" ry="1.438"/><ellipse class="e" cx="12" cy="13.938" rx="0.208" ry="1.438"/><ellipse class="e" cx="14.372" cy="13.938" rx="0.208" ry="1.438"/><ellipse class="e" cx="16.743" cy="13.938" rx="0.208" ry="1.438"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,7.313A1.747,1.747,0,1,0,8.8,9.059,1.747,1.747,0,0,0,7.055,7.313Z"/><path class="b" d="M16.958,7.313A1.747,1.747,0,1,0,18.7,9.059,1.747,1.747,0,0,0,16.958,7.313Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><ellipse class="b" cx="12" cy="13.375" rx="5.479" ry="0.297"/><ellipse class="c" cx="12" cy="14.646" rx="1.969" ry="0.229"/></svg>

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M472.096 270.486L278.946 470.196C266.304 483.268 245.616 483.268 232.974 470.196L39.824 270.486C-16.197 212.517 -13.104 116.653 49.041 62.862C103.316 15.884 186.371 24.359 236.32 75.925L256.007 96.249L275.694 75.925C325.641 24.36 408.694 15.884 462.969 62.859C525.118 116.649 528.212 212.515 472.096 270.486Z"/></svg>

After

Width:  |  Height:  |  Size: 388 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#e7c930;}.c{fill:#f06880;}.d{fill:#864e20;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M9.58,6.983A1.528,1.528,0,0,0,7.5,7.1l-.449.45L6.6,7.1a1.529,1.529,0,0,0-2.083-.113,1.472,1.472,0,0,0-.058,2.136L6.68,11.34a.518.518,0,0,0,.737,0l2.22-2.221A1.471,1.471,0,0,0,9.58,6.983Z"/><path class="c" d="M19.483,6.983A1.528,1.528,0,0,0,17.4,7.1l-.449.45L16.5,7.1a1.529,1.529,0,0,0-2.083-.113,1.471,1.471,0,0,0-.057,2.136l2.221,2.221a.517.517,0,0,0,.736,0l2.221-2.221A1.472,1.472,0,0,0,19.483,6.983Z"/><path class="d" d="M16.666,12.583H7.334a.493.493,0,0,0-.492.544c.123,1.175.875,3.842,5.158,3.842s5.035-2.667,5.158-3.842A.493.493,0,0,0,16.666,12.583Z"/><path class="c" d="M12,16.969a6.538,6.538,0,0,0,2.959-.6,1.979,1.979,0,0,0-1.209-.853c-1.344-.3-1.75.109-1.75.109s-.406-.406-1.75-.109a1.979,1.979,0,0,0-1.209.853A6.538,6.538,0,0,0,12,16.969Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#e7c930;}.c{fill:#864e20;}.d{fill:#f06880;}.e{fill:#009345;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M16.666,12.583H7.334a.493.493,0,0,0-.492.544c.123,1.175.875,3.842,5.158,3.842s5.035-2.667,5.158-3.842A.493.493,0,0,0,16.666,12.583Z"/><path class="d" d="M12,16.969a6.538,6.538,0,0,0,2.959-.6,1.979,1.979,0,0,0-1.209-.853c-1.344-.3-1.75.109-1.75.109s-.406-.406-1.75-.109a1.979,1.979,0,0,0-1.209.853A6.538,6.538,0,0,0,12,16.969Z"/><path class="e" d="M8.82,8.767h0a1.18,1.18,0,0,0-.378-.414A1.946,1.946,0,0,0,7.906,8.1,4.37,4.37,0,0,0,7.462,8a.094.094,0,0,1-.079-.09V6.692a.1.1,0,0,1,.036-.074A.081.081,0,0,1,7.488,6.6a.882.882,0,0,1,.375.173.579.579,0,0,1,.189.385.344.344,0,0,0,.337.3H8.5a.342.342,0,0,0,.256-.116.336.336,0,0,0,.084-.262,1.536,1.536,0,0,0-.1-.407A1.22,1.22,0,0,0,8.4,6.208a1.457,1.457,0,0,0-.5-.273,2.3,2.3,0,0,0-.44-.092.09.09,0,0,1-.077-.089V5.508a.341.341,0,0,0-.682,0v.248a.094.094,0,0,1-.086.091,1.848,1.848,0,0,0-.975.381,1.251,1.251,0,0,0-.415,1.024,1.365,1.365,0,0,0,.146.624,1.185,1.185,0,0,0,.364.409,1.711,1.711,0,0,0,.506.238c.123.036.253.069.385.1a.091.091,0,0,1,.075.088V9.944a.092.092,0,0,1-.034.072.088.088,0,0,1-.073.019,1.089,1.089,0,0,1-.45-.189.575.575,0,0,1-.209-.39A.339.339,0,0,0,5.6,9.163H5.482a.342.342,0,0,0-.34.383,1.254,1.254,0,0,0,.426.853,1.934,1.934,0,0,0,1.056.385.092.092,0,0,1,.077.093v.234a.341.341,0,0,0,.682,0v-.246a.094.094,0,0,1,.088-.091,2.069,2.069,0,0,0,1.02-.358,1.208,1.208,0,0,0,.467-1.03A1.29,1.29,0,0,0,8.82,8.767Zm-.659.611a.663.663,0,0,1-.064.306.515.515,0,0,1-.175.2.941.941,0,0,1-.287.125c-.048.013-.1.023-.148.032a.084.084,0,0,1-.07-.019.1.1,0,0,1-.034-.072V8.855a.091.091,0,0,1,.034-.07.093.093,0,0,1,.059-.021l.02,0,.125.028a1.062,1.062,0,0,1,.295.116.512.512,0,0,1,.181.18A.577.577,0,0,1,8.161,9.378ZM6.7,6.686V7.878c-.067-.013-.131-.027-.192-.044a.9.9,0,0,1-.259-.115.479.479,0,0,1-.162-.178.651.651,0,0,1-.058-.3.556.556,0,0,1,.2-.49A1.026,1.026,0,0,1,6.592,6.6l.02,0a.087.087,0,0,1,.053.019A.1.1,0,0,1,6.7,6.686Z"/><path class="e" d="M18.723,8.767a1.18,1.18,0,0,0-.378-.414,1.946,1.946,0,0,0-.536-.249A4.37,4.37,0,0,0,17.365,8a.093.093,0,0,1-.078-.09V6.692a.093.093,0,0,1,.036-.074.078.078,0,0,1,.068-.017.892.892,0,0,1,.376.173.582.582,0,0,1,.188.385.345.345,0,0,0,.337.3H18.4a.342.342,0,0,0,.256-.116.335.335,0,0,0,.084-.262,1.536,1.536,0,0,0-.1-.407,1.22,1.22,0,0,0-.332-.463,1.457,1.457,0,0,0-.5-.273,2.3,2.3,0,0,0-.44-.092.089.089,0,0,1-.076-.089V5.508a.342.342,0,0,0-.683,0v.248a.094.094,0,0,1-.086.091,1.848,1.848,0,0,0-.975.381,1.253,1.253,0,0,0-.415,1.024,1.368,1.368,0,0,0,.146.624,1.185,1.185,0,0,0,.364.409,1.711,1.711,0,0,0,.506.238c.123.036.253.069.385.1a.091.091,0,0,1,.075.088V9.944a.092.092,0,0,1-.034.072.088.088,0,0,1-.073.019,1.089,1.089,0,0,1-.45-.189.579.579,0,0,1-.209-.39.338.338,0,0,0-.336-.293h-.117a.342.342,0,0,0-.34.383,1.258,1.258,0,0,0,.426.853,1.934,1.934,0,0,0,1.056.385.092.092,0,0,1,.077.093v.234a.342.342,0,0,0,.683,0v-.246a.093.093,0,0,1,.087-.091,2.066,2.066,0,0,0,1.02-.358,1.208,1.208,0,0,0,.467-1.03A1.29,1.29,0,0,0,18.723,8.767Zm-.659.611A.663.663,0,0,1,18,9.684a.515.515,0,0,1-.175.2.93.93,0,0,1-.287.125c-.048.012-.1.023-.147.032a.086.086,0,0,1-.071-.019.094.094,0,0,1-.033-.072V8.855a.091.091,0,0,1,.033-.07.093.093,0,0,1,.059-.021l.02,0,.125.028a1.05,1.05,0,0,1,.295.116A.5.5,0,0,1,18,9.09.566.566,0,0,1,18.064,9.378ZM16.6,6.686V7.878c-.067-.013-.131-.027-.192-.044a.89.89,0,0,1-.258-.115.463.463,0,0,1-.162-.178.637.637,0,0,1-.059-.3.559.559,0,0,1,.2-.49A1.026,1.026,0,0,1,16.5,6.6l.02,0a.087.087,0,0,1,.053.019A.1.1,0,0,1,16.6,6.686Z"/></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#e7c930;}.c{fill:#864e20;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M18.29,9.084h-4.1a.586.586,0,0,1,0-1.171h4.1a.586.586,0,0,1,0,1.171Z"/><path class="c" d="M9.806,9.084H5.71a.586.586,0,0,1,0-1.171h4.1a.586.586,0,0,1,0,1.171Z"/><path class="c" d="M8.672,10.205A1.706,1.706,0,0,0,10.379,8.5H6.966A1.706,1.706,0,0,0,8.672,10.205Z"/><path class="c" d="M17.156,10.205A1.706,1.706,0,0,0,18.863,8.5H15.45A1.706,1.706,0,0,0,17.156,10.205Z"/><ellipse class="c" cx="12" cy="13.375" rx="5.479" ry="0.297"/><ellipse class="b" cx="12" cy="14.646" rx="1.969" ry="0.229"/></svg>

After

Width:  |  Height:  |  Size: 870 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#009345;}.b{fill:#017b3f;}.c{fill:#08512a;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M9.6,8.833,9.021,8.6c-.35-.144-.7-.283-1.058-.412s-.716-.251-1.08-.362-.731-.212-1.1-.3l-.012,0a.246.246,0,0,0-.186.448l.01.006c.325.2.656.392.991.573q.281.15.564.291a.245.245,0,0,1,0,.439q-.285.141-.564.292c-.335.18-.667.369-.992.573l-.016.01a.246.246,0,0,0,.187.447l.018,0c.374-.088.741-.19,1.105-.3s.723-.234,1.079-.362c.179-.064.355-.134.532-.2l.526-.213.573-.232A.246.246,0,0,0,9.6,8.833Z"/><path class="c" d="M14.405,8.833l.574-.235c.35-.144.7-.283,1.058-.412s.716-.251,1.08-.362.731-.212,1.1-.3l.012,0a.246.246,0,0,1,.186.448l-.01.006c-.325.2-.656.392-.991.573q-.28.15-.564.291a.245.245,0,0,0,0,.439q.285.141.564.292c.335.18.667.369.992.573l.016.01a.246.246,0,0,1-.187.447l-.018,0c-.374-.088-.741-.19-1.105-.3s-.723-.234-1.079-.362c-.179-.064-.355-.134-.532-.2l-.526-.213-.573-.232A.246.246,0,0,1,14.405,8.833Z"/><path class="c" d="M17.051,13.32c-.139-.122-.276-.247-.418-.366-.28-.241-.566-.476-.852-.708h0a.216.216,0,0,0-.245-.019l-.034.019c-.319.185-.636.372-.951.564-.2.119-.394.241-.59.364a.218.218,0,0,1-.243-.009l-.134-.1c-.149-.109-.3-.214-.449-.322-.3-.214-.6-.422-.9-.632l-.106-.074a.217.217,0,0,0-.248,0l-.106.074c-.3.21-.606.417-.9.632-.15.107-.3.212-.449.321l-.134.1a.218.218,0,0,1-.243.008c-.2-.122-.391-.244-.589-.363-.157-.1-.316-.19-.474-.285s-.319-.186-.478-.279l-.033-.019a.214.214,0,0,0-.245.019h0c-.287.232-.572.467-.853.709-.141.119-.278.244-.418.366s-.275.249-.41.377c.168-.08.335-.161.5-.247s.33-.169.492-.258c.228-.121.453-.247.677-.374a.217.217,0,0,1,.242.02l.2.16c.145.113.289.228.436.339.291.225.587.446.882.665l.074.055a.215.215,0,0,0,.246.008l.1-.063.464-.3c.155-.1.307-.2.461-.3.19-.124.377-.25.565-.378a.219.219,0,0,1,.243,0c.187.127.374.254.564.377.154.1.306.2.462.3l.464.3.1.063a.215.215,0,0,0,.246-.008L14.03,14c.295-.219.59-.44.882-.665.214-.165.426-.332.638-.5a.215.215,0,0,1,.241-.019c.225.127.451.253.68.376.162.089.328.171.492.258s.331.167.5.247C17.327,13.569,17.191,13.442,17.051,13.32Z"/><path class="c" d="M17.051,12.78c-.139.122-.276.247-.418.366-.28.241-.566.476-.852.708h0a.218.218,0,0,1-.245.019l-.034-.019c-.319-.185-.636-.372-.951-.564-.2-.119-.394-.241-.59-.364a.218.218,0,0,0-.243.009l-.134.1c-.149.109-.3.214-.449.321-.3.215-.6.423-.9.633l-.106.074a.217.217,0,0,1-.248,0l-.106-.074c-.3-.21-.606-.417-.9-.632-.15-.107-.3-.212-.449-.321l-.134-.1a.218.218,0,0,0-.243-.008c-.2.122-.391.244-.589.363-.157.1-.316.19-.474.285s-.319.186-.478.279l-.033.019a.214.214,0,0,1-.245-.019h0c-.287-.232-.572-.467-.853-.709-.141-.119-.278-.244-.418-.366s-.275-.249-.41-.377c.168.08.335.161.5.247s.33.169.492.258c.228.121.453.247.677.374a.217.217,0,0,0,.242-.02c.067-.054.135-.107.2-.16.145-.113.289-.228.436-.339.291-.225.587-.446.882-.665l.074-.055a.217.217,0,0,1,.246-.009l.1.064.464.3c.155.1.307.2.461.3.19.124.377.25.565.377a.217.217,0,0,0,.243,0c.187-.127.374-.254.564-.377.154-.1.306-.2.462-.3l.464-.3.1-.062a.215.215,0,0,1,.246.008l.074.054c.295.22.59.441.882.666.214.165.426.332.638.5a.215.215,0,0,0,.241.019c.225-.127.451-.253.68-.376.162-.089.328-.171.492-.258s.331-.167.5-.247C17.327,12.531,17.191,12.658,17.051,12.78Z"/></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,7.313A1.747,1.747,0,1,0,8.8,9.059,1.747,1.747,0,0,0,7.055,7.313Z"/><path class="b" d="M16.958,7.313A1.747,1.747,0,1,0,18.7,9.059,1.747,1.747,0,0,0,16.958,7.313Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="b" d="M12.991,13.937l1.091-1.09a.555.555,0,0,0,0-.785l-.206-.207a.556.556,0,0,0-.785,0L12,12.946l-1.091-1.091a.556.556,0,0,0-.785,0l-.206.207a.555.555,0,0,0,0,.785l1.091,1.09L9.918,15.028a.555.555,0,0,0,0,.785l.206.207a.556.556,0,0,0,.785,0L12,14.929l1.091,1.091a.556.556,0,0,0,.785,0l.206-.207a.555.555,0,0,0,0-.785Z"/></svg>

After

Width:  |  Height:  |  Size: 882 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,7.313A1.747,1.747,0,1,0,8.8,9.059,1.747,1.747,0,0,0,7.055,7.313Z"/><path class="b" d="M16.958,7.313A1.747,1.747,0,1,0,18.7,9.059,1.747,1.747,0,0,0,16.958,7.313Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="b" d="M16.53,12.324a8.617,8.617,0,0,1-.494.726,5.59,5.59,0,0,1-1.029,1.058,4.794,4.794,0,0,1-.6.412,1.6,1.6,0,0,1-.162.091c-.055.028-.109.061-.164.09-.115.051-.226.115-.346.163-.26.119-.533.223-.819.329a.231.231,0,0,0,.055.446,3.783,3.783,0,0,0,.979-.022,3.484,3.484,0,0,0,.878-.25,3.718,3.718,0,0,0,.409-.205l.012-.007a4.1,4.1,0,0,0,.379-.26,3.51,3.51,0,0,0,1.1-1.465,3.381,3.381,0,0,0,.222-.871c0-.031.006-.061.009-.092A.231.231,0,0,0,16.53,12.324Z"/></svg>

After

Width:  |  Height:  |  Size: 1015 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}.d{fill:#f06880;}.e{fill:#26a9e0;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M8.907,9.844a.182.182,0,0,1-.331.1,2.016,2.016,0,0,0-.569-.567,1.731,1.731,0,0,0-1.915,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,8.907,9.844Z"/><path class="b" d="M18.81,9.844a.182.182,0,0,1-.331.1,2.026,2.026,0,0,0-.568-.567,1.732,1.732,0,0,0-1.916,0,2.016,2.016,0,0,0-.571.569.182.182,0,0,1-.331-.1,1.632,1.632,0,0,1,.346-1.023,1.927,1.927,0,0,1,3.026,0A1.64,1.64,0,0,1,18.81,9.844Z"/><path class="c" d="M23,13.938a14.688,14.688,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="b" d="M16.666,12.583H7.334a.493.493,0,0,0-.492.544c.123,1.175.875,3.842,5.158,3.842s5.035-2.667,5.158-3.842A.493.493,0,0,0,16.666,12.583Z"/><path class="d" d="M12,16.969a6.538,6.538,0,0,0,2.959-.6,1.979,1.979,0,0,0-1.209-.853c-1.344-.3-1.75.109-1.75.109s-.406-.406-1.75-.109a1.979,1.979,0,0,0-1.209.853A6.538,6.538,0,0,0,12,16.969Z"/><path class="e" d="M5.117,15.682a6.6,6.6,0,0,0,1.311-4.356,6.6,6.6,0,0,0-4.357,1.31c-1.77,1.523-.92,3.011-.442,3.489S3.594,17.453,5.117,15.682Z"/><path class="e" d="M18.883,15.682a6.6,6.6,0,0,1-1.311-4.356,6.6,6.6,0,0,1,4.357,1.31c1.77,1.523.92,3.011.442,3.489S20.406,17.453,18.883,15.682Z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.fa-secondary{opacity:.4}</style></defs><path d="M512 224.112C512 197.608 490.516 176.133 464 176.133H317.482C340.25 138.226 352.005 95.257 352.005 80.11C352.005 56.523 333.495 32 302.54 32C239.411 32 276.176 108.148 194.312 173.618L178.016 186.644C166.23 196.06 160.285 209.903 160.215 223.897C160.191 223.921 160 224.112 160 224.112V384.042C160 399.146 167.113 413.368 179.198 422.427L213.336 448.02C241.027 468.779 274.702 480 309.309 480H368C394.516 480 416 458.525 416 432.021C416 428.386 415.52 424.878 414.754 421.475C434 415.228 448 397.37 448 376.045C448 366.897 445.303 358.438 440.861 351.164C463.131 347.002 480 327.547 480 304.077C480 291.577 475.107 280.298 467.275 271.761C492.234 270.051 512 249.495 512 224.112Z" class="fa-secondary"/><path d="M128 448V224C128 206.328 113.674 192 96 192H32C14.326 192 0 206.328 0 224V448C0 465.674 14.326 480 32 480H96C113.674 480 128 465.674 128 448Z" class="fa-primary"/></svg>

After

Width:  |  Height:  |  Size: 1005 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#e7c930;}.c{fill:#864e20;}.d{fill:#f06880;}.e{fill:#cd4b68;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="c" d="M16.666,12.583H7.334a.493.493,0,0,0-.492.544c.123,1.175.875,3.842,5.158,3.842s5.035-2.667,5.158-3.842A.493.493,0,0,0,16.666,12.583Z"/><path class="d" d="M15.405,15.136a2.463,2.463,0,0,0-1.655-1.87c-1.344-.3-1.75.109-1.75.109s-.406-.406-1.75-.109A2.463,2.463,0,0,0,8.6,15.136a8.449,8.449,0,0,0,0,2.723c.264,1.172,1.061,1.61,3.4,1.61s3.141-.438,3.4-1.61A8.449,8.449,0,0,0,15.405,15.136Z"/><path class="e" d="M12.188,15.688a5.582,5.582,0,0,1,.624-2.529,1.228,1.228,0,0,0-.812.216,1.228,1.228,0,0,0-.812-.216,5.582,5.582,0,0,1,.624,2.529C11.771,17,12,17,12,17S12.229,17,12.188,15.688Z"/><path class="c" d="M9.6,8.833,9.021,8.6c-.35-.144-.7-.283-1.058-.412s-.716-.251-1.08-.362-.731-.212-1.1-.3l-.012,0a.246.246,0,0,0-.186.448l.01.006c.325.2.656.392.991.573q.281.15.564.291a.245.245,0,0,1,0,.439q-.285.141-.564.292c-.335.18-.667.369-.992.573l-.016.01a.246.246,0,0,0,.187.447l.018,0c.374-.088.741-.19,1.105-.3s.723-.234,1.079-.362c.179-.064.355-.134.532-.2l.526-.213.573-.232A.246.246,0,0,0,9.6,8.833Z"/><path class="c" d="M14.405,8.833l.574-.235c.35-.144.7-.283,1.058-.412s.716-.251,1.08-.362.731-.212,1.1-.3l.012,0a.246.246,0,0,1,.186.448l-.01.006c-.325.2-.656.392-.991.573q-.28.15-.564.291a.245.245,0,0,0,0,.439q.285.141.564.292c.335.18.667.369.992.573l.016.01a.246.246,0,0,1-.187.447l-.018,0c-.374-.088-.741-.19-1.105-.3s-.723-.234-1.079-.362c-.179-.064-.355-.134-.532-.2l-.526-.213-.573-.232A.246.246,0,0,1,14.405,8.833Z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,16.688A1.747,1.747,0,1,1,8.8,14.941,1.748,1.748,0,0,1,7.055,16.688Z"/><path class="b" d="M16.958,16.688A1.747,1.747,0,1,1,18.7,14.941,1.748,1.748,0,0,1,16.958,16.688Z"/><path class="b" d="M14,12.793a.32.32,0,0,1-.313-.327,2.1,2.1,0,0,0-.5-1.33A1.593,1.593,0,0,0,12,10.7a1.6,1.6,0,0,0-1.187.43,2.088,2.088,0,0,0-.5,1.334.32.32,0,1,1-.64.012,2.712,2.712,0,0,1,.679-1.791A2.211,2.211,0,0,1,12,10.063a2.211,2.211,0,0,1,1.647.625,2.718,2.718,0,0,1,.679,1.791A.322.322,0,0,1,14,12.793Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/></svg>

After

Width:  |  Height:  |  Size: 868 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.a{fill:#f8de40;}.b{fill:#864e20;}.c{fill:#e7c930;}.d{fill:#f06880;}</style></defs><rect class="a" x="1" y="1" width="22" height="22" rx="7.656"/><path class="b" d="M7.055,7.313A1.747,1.747,0,1,0,8.8,9.059,1.747,1.747,0,0,0,7.055,7.313Z"/><path class="b" d="M16.958,7.313A1.747,1.747,0,1,0,18.7,9.059,1.747,1.747,0,0,0,16.958,7.313Z"/><path class="c" d="M23,13.938a14.69,14.69,0,0,1-12.406,6.531c-5.542,0-6.563-1-9.142-2.529A7.66,7.66,0,0,0,8.656,23h6.688A7.656,7.656,0,0,0,23,15.344Z"/><path class="b" d="M16.083,12.556A5.487,5.487,0,0,0,12,10.806a5.487,5.487,0,0,0-4.083,1.75c-.959,1.292-.147,2.667.885,2.583s2.781-.285,3.2-.285,2.167.2,3.2.285S17.042,13.848,16.083,12.556Z"/><path class="d" d="M13.75,13.266c-1.344-.3-1.75.109-1.75.109s-.406-.406-1.75-.109A2.463,2.463,0,0,0,8.6,15.136a1.1,1.1,0,0,0,.207,0c1.031-.083,2.781-.285,3.2-.285s2.167.2,3.2.285a1.1,1.1,0,0,0,.207,0A2.463,2.463,0,0,0,13.75,13.266Z"/><path class="c" d="M13.965,15.91a9.842,9.842,0,0,0-1.965-.3,9.842,9.842,0,0,0-1.965.3c-.294.061-.3.3,0,.261S12,16.041,12,16.041s1.663.09,1.965.13S14.259,15.971,13.965,15.91Z"/><path class="b" d="M19.686,6.658l0,0a2.954,2.954,0,0,0-.228-.385,4.467,4.467,0,0,0-.576-.675c-.108-.1-.217-.205-.332-.3s-.242-.174-.364-.26A3.4,3.4,0,0,0,17.8,4.8c-.134-.066-.263-.143-.4-.2a4.857,4.857,0,0,0-1.743-.4,3.732,3.732,0,0,0-1.334.177.174.174,0,0,0,.007.327c.406.139.784.271,1.157.41.494.184.973.367,1.442.576.121.043.233.107.351.158l.178.076c.06.025.114.059.174.085.116.054.23.112.35.161l.011,0c.114.06.229.119.348.175.247.105.476.244.735.355.128.06.254.124.386.186A.173.173,0,0,0,19.686,6.658Z"/><path class="b" d="M9.691,4.38A3.729,3.729,0,0,0,8.357,4.2a4.862,4.862,0,0,0-1.743.4c-.139.055-.269.132-.4.2a3.4,3.4,0,0,0-.384.231c-.122.086-.246.169-.363.26s-.224.2-.332.3a4.474,4.474,0,0,0-.577.675,2.948,2.948,0,0,0-.227.385l0,0a.173.173,0,0,0,.227.236c.131-.062.258-.126.386-.186.259-.111.487-.25.734-.355.119-.056.235-.115.349-.175l.01,0c.12-.049.235-.107.35-.161.06-.026.115-.06.174-.085l.178-.076c.118-.051.231-.115.352-.158.469-.209.947-.392,1.441-.576.373-.139.751-.271,1.158-.41A.174.174,0,0,0,9.691,4.38Z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,24 @@
import DeletionForm from "~/components/DeletionForm";
export default function LifeAndLinageDeletionForm() {
return (
<div class="pt-20">
<div class="container mx-auto p-4 md:p-6 lg:p-12">
<div class="w-full justify-center">
<div class="text-xl">
<em>What will happen</em>:
</div>
Once you send, if a match to the email provided is found in our
system, a 24hr grace period is started where you can request a
cancellation of the account deletion. Once the grace period ends, the
account's entry in our central database will be completely
removed, and your individual database storing your remote saves will
also be deleted. No data related to the account is retained in any
way.
</div>
<DeletionForm />
</div>
</div>
);
}

View File

@@ -6,6 +6,7 @@ import { env } from "~/env/server";
import { TRPCError } from "@trpc/server";
import { ConnectionFactory } from "~/server/utils";
import * as bcrypt from "bcrypt";
import { getCookie, setCookie } from "vinxi/http";
const assets: Record<string, string> = {
"shapes-with-abigail": "shapes-with-abigail.apk",
@@ -201,4 +202,91 @@ export const miscRouter = createTRPCRouter({
});
}
}),
// ============================================================
// Account Deletion Request
// ============================================================
sendDeletionRequestEmail: publicProcedure
.input(z.object({ email: z.string().email() }))
.mutation(async ({ input }) => {
// Check if deletion request was recently sent
const deletionExp = getCookie("deletionRequestSent");
let remaining = 0;
if (deletionExp) {
const expires = new Date(deletionExp);
remaining = expires.getTime() - Date.now();
}
if (remaining > 0) {
throw new TRPCError({
code: "TOO_MANY_REQUESTS",
message: "countdown not expired",
});
}
const apiKey = env.SENDINBLUE_KEY;
const apiUrl = "https://api.sendinblue.com/v3/smtp/email";
// Email to admin
const sendinblueMyData = {
sender: {
name: "freno.me",
email: "michael@freno.me",
},
to: [{ email: "michael@freno.me" }],
htmlContent: `<html><head></head><body><div>Request Name: Life and Lineage Account Deletion</div><div>Request Email: ${input.email}</div></body></html>`,
subject: "Life and Lineage Acct Deletion",
};
// Email to user
const sendinblueUserData = {
sender: {
name: "freno.me",
email: "michael@freno.me",
},
to: [{ email: input.email }],
htmlContent: `<html><head></head><body><div>Request Name: Life and Lineage Account Deletion</div><div>Account to delete: ${input.email}</div><div>You can email michael@freno.me in the next 24hrs to cancel the deletion, email with subject line "Account Deletion Cancellation"</div></body></html>`,
subject: "Life and Lineage Acct Deletion",
};
try {
// Send both emails
await fetch(apiUrl, {
method: "POST",
headers: {
accept: "application/json",
"api-key": apiKey,
"content-type": "application/json",
},
body: JSON.stringify(sendinblueMyData),
});
await fetch(apiUrl, {
method: "POST",
headers: {
accept: "application/json",
"api-key": apiKey,
"content-type": "application/json",
},
body: JSON.stringify(sendinblueUserData),
});
// Set cookie to prevent spam (60 second cooldown)
const exp = new Date(Date.now() + 1 * 60 * 1000);
setCookie("deletionRequestSent", exp.toUTCString(), {
expires: exp,
path: "/",
});
return { message: "request sent" };
} catch (error) {
console.error(error);
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "SMTP server error: Sorry! You can reach me at michael@freno.me",
});
}
}),
});