improve site and resource info cards and other small visual tweaks

This commit is contained in:
Milo Schwartz 2024-12-30 23:41:06 -05:00
parent e6263567a9
commit 172e0f07d5
No known key found for this signature in database
31 changed files with 469 additions and 332 deletions

View file

@ -33,10 +33,20 @@ export const ConfirmPasswordReset = ({ email }: Props) => {
}
}}
>
<Body className="font-sans">
<Body className="font-sans relative">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
Your password has been successfully reset
Password Reset Confirmation
</Heading>
<Text className="text-base text-gray-700 mt-4">
Hi {email || "there"},
@ -46,12 +56,10 @@ export const ConfirmPasswordReset = ({ email }: Props) => {
reset. If you made this change, no further action is
required.
</Text>
<Section className="text-center my-6">
<Text className="text-base text-gray-700">
If you did not request this change, please
contact our support team immediately.
</Text>
</Section>
<Text className="text-base text-gray-700">
If you did not request this change, please contact
our support team immediately.
</Text>
<Text className="text-base text-gray-700 mt-2">
Thank you for keeping your account secure.
</Text>

View file

@ -37,8 +37,18 @@ export const ResetPasswordCode = ({ email, code, link }: Props) => {
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
You've requested to reset your password
Password Reset Request
</Heading>
<Text className="text-base text-gray-700 mt-4">
Hi {email || "there"},
@ -51,7 +61,7 @@ export const ResetPasswordCode = ({ email, code, link }: Props) => {
and follow the instructions to reset your password,
or manually enter the following code:
</Text>
<Section className="text-center my-6">
<Section className="text-center">
<Text className="inline-block bg-primary text-xl font-bold text-white py-2 px-4 border border-gray-300 rounded-xl">
{code}
</Text>

View file

@ -43,6 +43,16 @@ export const ResourceOTPCode = ({
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
Your One-Time Password
</Heading>
@ -56,12 +66,11 @@ export const ResourceOTPCode = ({
<strong>{organizationName}</strong>. Use the OTP
below to complete your authentication:
</Text>
<Section className="text-center my-6">
<Section className="text-center">
<Text className="inline-block bg-primary text-xl font-bold text-white py-2 px-4 border border-gray-300 rounded-xl">
{otp}
</Text>
</Section>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />

View file

@ -46,8 +46,18 @@ export const SendInviteLink = ({
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
You're invited to join a Fossorial organization
You're Invite to Join {orgName}
</Heading>
<Text className="text-base text-gray-700 mt-4">
Hi {email || "there"},
@ -65,12 +75,12 @@ export const SendInviteLink = ({
{expiresInDays === "1" ? "day" : "days"}.
</b>
</Text>
<Section className="text-center my-6">
<Section className="text-center">
<Button
href={inviteLink}
className="rounded-lg bg-primary px-[12px] py-[9px] text-center font-semibold text-white cursor-pointer text-xl"
>
Accept invitation to {orgName}
Accept Invite to {orgName}
</Button>
</Section>

View file

@ -36,6 +36,16 @@ export const TwoFactorAuthNotification = ({ email, enabled }: Props) => {
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
Two-Factor Authentication{" "}
{enabled ? "Enabled" : "Disabled"}
@ -48,22 +58,19 @@ export const TwoFactorAuthNotification = ({ email, enabled }: Props) => {
has been successfully{" "}
{enabled ? "enabled" : "disabled"} on your account.
</Text>
<Section className="text-center my-6">
{enabled ? (
<Text className="text-base text-gray-700">
With Two-Factor Authentication enabled, your
account is now more secure. Please ensure
you keep your authentication method safe.
</Text>
) : (
<Text className="text-base text-gray-700">
With Two-Factor Authentication disabled,
your account may be less secure. We
recommend enabling it to protect your
account.
</Text>
)}
</Section>
{enabled ? (
<Text className="text-base text-gray-700">
With Two-Factor Authentication enabled, your
account is now more secure. Please ensure you
keep your authentication method safe.
</Text>
) : (
<Text className="text-base text-gray-700">
With Two-Factor Authentication disabled, your
account may be less secure. We recommend
enabling it to protect your account.
</Text>
)}
<Text className="text-base text-gray-700 mt-2">
If you did not make this change, please contact our
support team immediately.

View file

@ -41,17 +41,28 @@ export const VerifyEmail = ({
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-orange-500">
Pangolin
</div>
<div className="text-sm text-gray-500">
{new Date().toLocaleDateString()}
</div>
</div>
<Heading className="text-2xl font-semibold text-gray-800 text-center">
Please verify your email
Please Verify Your Email
</Heading>
<Text className="text-base text-gray-700 mt-4">
Hi {username || "there"},
</Text>
<Text className="text-base text-gray-700 mt-2">
Youve requested to verify your email. Please use
the code below to complete the verification process upon logging in.
the code below to complete the verification process
upon logging in.
</Text>
<Section className="text-center my-6">
<Section className="text-center">
<Text className="inline-block bg-primary text-xl font-bold text-white py-2 px-4 border border-gray-300 rounded-xl">
{verificationCode}
</Text>

View file

@ -25,7 +25,12 @@ const createSiteSchema = z
.object({
name: z.string().min(1).max(255),
exitNodeId: z.number().int().positive(),
subdomain: z.string().min(1).max(255).optional(),
// subdomain: z
// .string()
// .min(1)
// .max(255)
// .transform((val) => val.toLowerCase())
// .optional(),
pubKey: z.string().optional(),
subnet: z.string(),
newtId: z.string().optional(),

View file

@ -23,13 +23,25 @@ const getSiteSchema = z
})
.strict();
export type GetSiteResponse = {
siteId: number;
name: string;
subdomain: string;
subnet: string;
type: string;
};
async function query(siteId?: number, niceId?: string, orgId?: string) {
if (siteId) {
const [res] = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
return res;
} else if (niceId && orgId) {
const [res] = await db
.select()
.from(sites)
.where(and(eq(sites.niceId, niceId), eq(sites.orgId, orgId)))
.limit(1);
return res;
}
}
export type GetSiteResponse = NonNullable<Awaited<ReturnType<typeof query>>>;
export async function getSite(
req: Request,
@ -49,42 +61,14 @@ export async function getSite(
const { siteId, niceId, orgId } = parsedParams.data;
let site;
if (siteId) {
site = await db
.select()
.from(sites)
.where(eq(sites.siteId, siteId))
.limit(1);
} else if (niceId && orgId) {
site = await db
.select()
.from(sites)
.where(and(eq(sites.niceId, niceId), eq(sites.orgId, orgId)))
.limit(1);
}
const site = await query(siteId, niceId, orgId);
if (!site) {
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
}
if (site.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Site with ID ${siteId} not found`
)
);
}
return response(res, {
data: {
siteId: site[0].siteId,
niceId: site[0].niceId,
name: site[0].name,
subnet: site[0].subnet,
type: site[0].type
},
return response<GetSiteResponse>(res, {
data: site,
success: true,
error: false,
message: "Site retrieved successfully",

View file

@ -18,7 +18,12 @@ const updateSiteParamsSchema = z
const updateSiteBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
subdomain: z.string().min(1).max(255).optional()
// subdomain: z
// .string()
// .min(1)
// .max(255)
// .transform((val) => val.toLowerCase())
// .optional()
// pubKey: z.string().optional(),
// subnet: z.string().optional(),
// exitNode: z.number().int().positive().optional(),

View file

@ -6,4 +6,5 @@ export const subdomainSchema = z
/^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9-_]+$/,
"Invalid subdomain format"
)
.min(1, "Subdomain must be at least 1 character long");
.min(1, "Subdomain must be at least 1 character long")
.transform((val) => val.toLowerCase());