First Commit. Duplicate bunext-sqlite preset

This commit is contained in:
2026-10-18 16:47:11 +01:00
commit 81e4a8aecc
718 changed files with 41695 additions and 0 deletions
@@ -0,0 +1,29 @@
import { CompanyLetterheadDefaults } from "@/src/dict/company-dict";
import { Mail, MapPin, Phone, type LucideIcon } from "lucide-react";
export type ContactChannel = {
Icon: LucideIcon;
label: string;
value: string;
href?: string;
};
export const ContactChannels: ContactChannel[] = [
{
Icon: Mail,
label: "Email",
value: CompanyLetterheadDefaults["email"],
href: `mailto:${CompanyLetterheadDefaults["email"]}`,
},
{
Icon: Phone,
label: "Phone",
value: CompanyLetterheadDefaults["phone"],
href: `tel:${CompanyLetterheadDefaults["phone"].replace(/\s/g, "")}`,
},
{
Icon: MapPin,
label: "Office",
value: CompanyLetterheadDefaults["address_lines"].join(", "),
},
];
@@ -0,0 +1,43 @@
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type useFormInit from "@/src/hooks/use-form-init";
import type { ApiReqParams, ContactFormObject } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function submitContactForm({
setLoading,
setStatus,
form,
}: ReturnType<typeof useFormInit<ContactFormObject>>) {
setLoading(true);
const res = await fetchApi<ApiReqParams, APIResponseObject>(
`/api/contact`,
{
method: "POST",
body: {
contact: {
name: form.name,
email: form.email,
subject: form.subject,
message: form.message,
},
},
},
);
if (res.success) {
setStatus({
success: true,
msg: res.msg || "Message sent successfully.",
});
} else {
setStatus({
error: true,
msg: res.msg || "Message Failed",
});
console.log("res", res);
console.log("form", form);
}
setLoading(false);
}
@@ -0,0 +1,44 @@
import Stack from "@moduletrace/twui/layout/Stack";
import Row from "@moduletrace/twui/layout/Row";
import Span from "@moduletrace/twui/layout/Span";
import Link from "@moduletrace/twui/layout/Link";
import type { ContactChannel } from "../(data)/contact-channels";
type Props = ContactChannel & {
index: number;
};
export default function ContactChannelItem({
Icon,
label,
value,
href,
index,
}: Props) {
return (
<Row className="w-full flex-nowrap items-start justify-between gap-6! py-6 border-t border-slate-300 dark:border-white/20">
<Stack className="gap-2! min-w-0">
<Span className="text-xs text-slate-400">
{String(index + 1).padStart(2, "0")} / {label}
</Span>
{href ? (
<Link
href={href}
className="text-lg font-medium tracking-tight text-foreground-light dark:text-foreground-dark border-0 leading-snug break-words hover:text-accent"
>
{value}
</Link>
) : (
<Span className="text-lg font-medium tracking-tight text-foreground-light dark:text-foreground-dark leading-snug">
{value}
</Span>
)}
</Stack>
<Icon
size={20}
strokeWidth={1.5}
className="text-slate-400 shrink-0 mt-1"
/>
</Row>
);
}
@@ -0,0 +1,28 @@
import Tag from "@moduletrace/twui/elements/Tag";
import Row from "@moduletrace/twui/layout/Row";
import Span from "@moduletrace/twui/layout/Span";
import type { TWUIStatusType } from "@moduletrace/twui/hooks/useStatus";
import { TriangleAlert } from "lucide-react";
type Props = {
status?: TWUIStatusType;
};
export default function ContactFormStatus({ status }: Props) {
if (!status?.error || !status.msg) {
return null;
}
return (
<Tag
variant="outlined"
color="error"
className="w-full py-1.5 outline-error/50 bg-error/5!"
>
<Row className="flex-nowrap gap-2!">
<TriangleAlert size={15} className="shrink-0" />
<Span>{status.msg}</Span>
</Row>
</Tag>
);
}
@@ -0,0 +1,34 @@
import Stack from "@moduletrace/twui/layout/Stack";
import Span from "@moduletrace/twui/layout/Span";
import H3 from "@moduletrace/twui/layout/H3";
import Button from "@moduletrace/twui/layout/Button";
import { CircleCheck, RotateCcw } from "lucide-react";
type Props = {
msg?: string;
onReset: () => void;
};
export default function ContactFormSuccess({ msg, onReset }: Props) {
return (
<Stack className="w-full items-start gap-6! py-6">
<CircleCheck size={28} strokeWidth={1.5} className="text-accent" />
<Stack className="gap-2!">
<H3>Message sent</H3>
<Span className="text-slate-500 dark:text-slate-400 leading-relaxed">
{msg || "Thanks for reaching out."} We'll reply to the
email address you provided.
</Span>
</Stack>
<Button
title="Send another message"
variant="outlined"
color="gray"
onClick={onReset}
beforeIcon={<RotateCcw size={15} />}
>
Send another
</Button>
</Stack>
);
}
@@ -0,0 +1,22 @@
import Button from "@moduletrace/twui/layout/Button";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
import { Send } from "lucide-react";
export default function ContactFormAction({}: ReturnType<
typeof useFormInit<ContactFormObject>
>) {
return (
<>
<Button
title="Submit Contact Form"
type="submit"
size="large"
className="w-full mt-2"
afterIcon={<Send size={17} />}
>
Send Message
</Button>
</>
);
}
@@ -0,0 +1,22 @@
import Input from "@moduletrace/twui/form/Input";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
export default function ContactFormEmail({
setForm,
}: ReturnType<typeof useFormInit<ContactFormObject>>) {
return (
<Input
type="email"
placeholder="Email"
onChange={(e) => {
setForm((prev) => ({
...prev,
email: e.target.value,
}));
}}
showLabel
required
/>
);
}
@@ -0,0 +1,23 @@
import Input from "@moduletrace/twui/form/Input";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
export default function ContactFormMessage({
setForm,
}: ReturnType<typeof useFormInit<ContactFormObject>>) {
return (
<Input
istextarea
rows={6}
placeholder="Message"
onChange={(e) => {
setForm((prev) => ({
...prev,
message: e.target.value,
}));
}}
showLabel
required
/>
);
}
@@ -0,0 +1,22 @@
import Input from "@moduletrace/twui/form/Input";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
export default function ContactFormName({
setForm,
}: ReturnType<typeof useFormInit<ContactFormObject>>) {
return (
<Input
type="text"
placeholder="Name"
onChange={(e) => {
setForm((prev) => ({
...prev,
name: e.target.value,
}));
}}
showLabel
required
/>
);
}
@@ -0,0 +1,21 @@
import Input from "@moduletrace/twui/form/Input";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
export default function ContactFormSubject({
setForm,
}: ReturnType<typeof useFormInit<ContactFormObject>>) {
return (
<Input
type="text"
placeholder="Subject (optional)"
onChange={(e) => {
setForm((prev) => ({
...prev,
subject: e.target.value,
}));
}}
showLabel
/>
);
}
@@ -0,0 +1,68 @@
import Form from "@moduletrace/twui/form/Form";
import LoadingOverlay from "@moduletrace/twui/elements/LoadingOverlay";
import Paper from "@moduletrace/twui/elements/Paper";
import Stack from "@moduletrace/twui/layout/Stack";
import Row from "@moduletrace/twui/layout/Row";
import H3 from "@moduletrace/twui/layout/H3";
import Span from "@moduletrace/twui/layout/Span";
import useFormInit from "@/src/hooks/use-form-init";
import { type ContactFormObject } from "@/src/types";
import submitContactForm from "../../(functions)/submit-contact-form";
import ContactFormName from "./contact-form-name";
import ContactFormEmail from "./contact-form-email";
import ContactFormSubject from "./contact-form-subject";
import ContactFormMessage from "./contact-form-message";
import ContactFormAction from "./contact-form-action";
import ContactFormStatus from "../contact-form-status";
import ContactFormSuccess from "../contact-form-success";
export default function ContactForm() {
const init = useFormInit<ContactFormObject>();
const { loading, status, setForm, setStatus } = init;
return (
<Paper className="relative w-full p-6 sm:p-10 rounded-default-xl bg-white">
{loading && <LoadingOverlay />}
<Stack className="w-full gap-8!">
<Row className="w-full items-start justify-between gap-4!">
<Stack className="gap-2!">
<H3>Send a message</H3>
<Span className="text-sm text-slate-500 dark:text-slate-400">
Fields marked * are required.
</Span>
</Stack>
<Span className="text-xs text-slate-400">
Form / 01
</Span>
</Row>
{status?.success ? (
<ContactFormSuccess
msg={status.msg}
onReset={() => {
setForm({} as ContactFormObject);
setStatus(undefined);
}}
/>
) : (
<Form
className="w-full"
onSubmit={() => {
submitContactForm(init);
}}
>
<Stack className="w-full items-stretch gap-5!">
<div className="grid w-full grid-cols-1 sm:grid-cols-2 gap-5">
<ContactFormName {...init} />
<ContactFormEmail {...init} />
</div>
<ContactFormSubject {...init} />
<ContactFormMessage {...init} />
<ContactFormStatus status={status} />
<ContactFormAction {...init} />
</Stack>
</Form>
)}
</Stack>
</Paper>
);
}
@@ -0,0 +1,33 @@
import Stack from "@moduletrace/twui/layout/Stack";
import Span from "@moduletrace/twui/layout/Span";
import H1 from "@moduletrace/twui/layout/H1";
import { ContactChannels } from "../(data)/contact-channels";
import ContactChannelItem from "./contact-channel-item";
export default function ContactInfo() {
return (
<Stack className="w-full gap-10! xl:sticky xl:top-28">
<Stack className="gap-8!">
<Span className="htag">Contact</Span>
<H1>
Let's talk.{" "}
<Span className="text-slate-400">We read everything.</Span>
</H1>
<Span className="text-lg text-slate-600 dark:text-slate-400 leading-relaxed max-w-md">
Questions, feedback or a project in mind. Send a note and a
real person will get back to you, usually within one
business day.
</Span>
</Stack>
<Stack className="w-full gap-0! border-b border-slate-300 dark:border-white/20">
{ContactChannels.map((channel, index) => (
<ContactChannelItem
key={channel.label}
{...channel}
index={index}
/>
))}
</Stack>
</Stack>
);
}
@@ -0,0 +1,17 @@
import Section from "@moduletrace/twui/layout/Section";
import Container from "@moduletrace/twui/layout/Container";
import ContactInfo from "../(partials)/contact-info";
import ContactForm from "../(partials)/contact-form";
export default function ContactSection() {
return (
<Section className="pt-20! xl:pt-32!">
<Container>
<div className="grid w-full grid-cols-1 xl:grid-cols-[1fr_minmax(0,560px)] items-start gap-16 xl:gap-24">
<ContactInfo />
<ContactForm />
</div>
</Container>
</Section>
);
}
+12
View File
@@ -0,0 +1,12 @@
import type { BunextPageModuleMeta } from "@moduletrace/bunext/types";
import ContactSection from "./(sections)/contact-section";
import { SiteData } from "@/src/data/site-data";
export default function ContactPage() {
return <ContactSection />;
}
export const meta: BunextPageModuleMeta = {
title: `Contact Us | ${SiteData["SiteName"]}`,
description: `Get in touch with ${SiteData["SiteName"]}. We'd love to hear from you.`,
};