Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ jobs:
- name: Create test database
run: pnpm run db:test:create

- name: Wait for test database
run: |
# TCP readiness excludes the socket-only initialization server.
for attempt in {1..30}; do
if docker exec servicestart-test-db \
pg_isready -h 127.0.0.1 -U test -d postgres; then
exit 0
fi
sleep 2
done
docker logs servicestart-test-db
exit 1

- name: Migrate test database
run: pnpm run db:test:migrate

Expand Down
30 changes: 12 additions & 18 deletions app/forgotpassword/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ import useOrganizationConfig from "@/lib/hooks/useOrganizationConfig";
import BogTextInput from "@/components/bog/BogTextInput/BogTextInput";
import BogButton from "@/components/bog/BogButton/BogButton";
import { OrganizationConfigKey } from "@/lib/schema";
import { useActiveOrganization } from "@/lib/hooks/useActiveOrganization";
import authClient from "@/lib/authClient";
import LeftArrowIcon from "@/components/LeftArrowIcon";
import UnauthenticatedOrganizationLogo from "@/components/UnauthenticatedOrganizationLogo";

export default function ForgotPasswordPage() {
const router = useRouter();
const [email, setEmail] = useState("");

const { primary_color = "#FFFFFF", secondary_color = "#FFFFFF" } =
useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
]);
const org = useActiveOrganization();
const logo = org?.organization.data?.logo;
const {
primary_color = "#FFFFFF",
secondary_color = "#FFFFFF",
logo_url: logoUrl,
} = useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
OrganizationConfigKey.LogoUrl,
]);

const handleEmailSubmit = async () => {
const { error } = await authClient.requestPasswordReset({
Expand All @@ -46,20 +48,12 @@ export default function ForgotPasswordPage() {
>
<div className="flex h-full w-1/2 flex-shrink-0 items-center justify-between px-8">
<div
className="flex h-[95%] w-full flex-col justify-flex-end rounded-3xl pb-5 pl-5 pr-1/2"
className="relative flex h-[95%] w-full flex-col justify-flex-end rounded-3xl pb-5 pl-5 pr-1/2"
style={{
background: `linear-gradient(180deg, ${primary_color} 0%, #FFF 100%)`,
}}
>
<div className="h-full w-1/3">
{logo && (
<img
src={`/images/${logo}`}
alt="Organization Logo"
className="h-[80%]"
/>
)}
</div>
<UnauthenticatedOrganizationLogo logoUrl={logoUrl} />
</div>
</div>
<div className="flex h-full flex-1 flex-col items-center justify-center">
Expand Down
24 changes: 11 additions & 13 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import authClient from "@/lib/authClient";
import useOrganizationConfig from "@/lib/hooks/useOrganizationConfig";
import BogTextInput from "@/components/bog/BogTextInput/BogTextInput";
import BogButton from "@/components/bog/BogButton/BogButton";
import UnauthenticatedOrganizationLogo from "@/components/UnauthenticatedOrganizationLogo";
import { OrganizationConfigKey } from "@/lib/schema";

import { useActiveOrganization } from "@/lib/hooks/useActiveOrganization";
Expand All @@ -20,14 +21,16 @@ export default function LoginPage() {
const {
primary_color = "#FFFFFF",
secondary_color = "#FFFFFF",
tagline = "Welcome",
tagline: configuredTagline,
logo_url: logoUrl,
} = useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
OrganizationConfigKey.Tagline,
OrganizationConfigKey.LogoUrl,
]);
const tagline = configuredTagline?.trim() || "Welcome";
const org = useActiveOrganization();
const logo = org?.organization.data?.logo;

const handleLogin = async () => {
setLoading(true);
Expand Down Expand Up @@ -78,26 +81,21 @@ export default function LoginPage() {
>
<div className="flex h-full w-[53%] flex-shrink-0 items-center justify-between px-[30px]">
<div
className="flex h-[94%] w-full flex-col justify-flex-end rounded-[20px] pt-[90%] pb-[20px] pl-[20px] pr-[60%]"
className="relative flex h-[94%] w-full flex-col justify-flex-end rounded-[20px] pt-[90%] pb-[20px] pl-[20px] pr-[60%]"
style={{
background: `linear-gradient(180deg, ${primary_color} 0%, #FFF 100%)`,
}}
>
<div className="h-[130px] w-[339px]">
{logo && (
<img
src={`/images/${logo}`}
alt="Organization Logo"
className="h-[107px] w-[107px]"
/>
)}
</div>
<UnauthenticatedOrganizationLogo logoUrl={logoUrl} />
</div>
</div>
<div className="flex h-full flex-1 flex-col items-center justify-between pt-[12%]">
<div className="flex w-[78%] bg-white flex-col items-center gap-6 rounded-4xl border-[2px] border-[#FFF] p-9 pt-25 shadow-[0_4px_7px_0_rgba(0,0,0,0.4)]">
<h1 className="self-stretch">Login</h1>
<p className="self-stretch text-mobile-heading-2 text-white">
<p
className="self-stretch text-mobile-heading-2 text-grey-text-strong"
data-testid="organization-tagline"
>
{tagline}
</p>
<BogTextInput
Expand Down
30 changes: 12 additions & 18 deletions app/resetpassword/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import useOrganizationConfig from "@/lib/hooks/useOrganizationConfig";
import BogTextInput from "@/components/bog/BogTextInput/BogTextInput";
import BogButton from "@/components/bog/BogButton/BogButton";
import { OrganizationConfigKey } from "@/lib/schema";
import { useActiveOrganization } from "@/lib/hooks/useActiveOrganization";
import authClient from "@/lib/authClient";
import XIcon from "@/components/XIcon";
import LeftArrowIcon from "@/components/LeftArrowIcon";
import UnauthenticatedOrganizationLogo from "@/components/UnauthenticatedOrganizationLogo";

export default function ResetPasswordPage() {
const router = useRouter();
Expand All @@ -19,13 +19,15 @@ export default function ResetPasswordPage() {
const [isExpired, setIsExpired] = useState(false);
const [passwordsMatch, setPasswordsMatch] = useState(true);

const { primary_color = "#FFFFFF", secondary_color = "#FFFFFF" } =
useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
]);
const org = useActiveOrganization();
const logo = org?.organization.data?.logo;
const {
primary_color = "#FFFFFF",
secondary_color = "#FFFFFF",
logo_url: logoUrl,
} = useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
OrganizationConfigKey.LogoUrl,
]);

const handleResetPassword = async () => {
const token = new URLSearchParams(window.location.search).get("token");
Expand Down Expand Up @@ -88,20 +90,12 @@ export default function ResetPasswordPage() {
>
<div className="flex h-full w-1/2 flex-shrink-0 items-center justify-between px-8">
<div
className="flex h-[95%] w-full flex-col justify-flex-end rounded-3xl pb-5 pl-5 pr-1/2"
className="relative flex h-[95%] w-full flex-col justify-flex-end rounded-3xl pb-5 pl-5 pr-1/2"
style={{
background: `linear-gradient(180deg, ${primary_color} 0%, #FFF 100%)`,
}}
>
<div className="h-full w-1/3">
{logo && (
<img
src={`/images/${logo}`}
alt="Organization Logo"
className="h-[80%]"
/>
)}
</div>
<UnauthenticatedOrganizationLogo logoUrl={logoUrl} />
</div>
</div>
{isExpired ? (
Expand Down
26 changes: 13 additions & 13 deletions app/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import BogTextInput from "@/components/bog/BogTextInput/BogTextInput";
import BogButton from "@/components/bog/BogButton/BogButton";
import authClient from "@/lib/authClient";
import useOrganizationConfig from "@/lib/hooks/useOrganizationConfig";
import UnauthenticatedOrganizationLogo from "@/components/UnauthenticatedOrganizationLogo";
import { OrganizationConfigKey } from "@/lib/schema";
import { getSlugFromHost } from "@/lib/clientAuthUtils";
import { useActiveOrganization } from "@/lib/hooks/useActiveOrganization";
Expand All @@ -21,14 +22,16 @@ export default function SignupPage() {
const {
primary_color = "#FFFFFF",
secondary_color = "#FFFFFF",
tagline = "Welcome",
tagline: configuredTagline,
logo_url: logoUrl,
} = useOrganizationConfig([
OrganizationConfigKey.PrimaryColor,
OrganizationConfigKey.SecondaryColor,
OrganizationConfigKey.Tagline,
OrganizationConfigKey.LogoUrl,
]);
const tagline = configuredTagline?.trim() || "Welcome";
const org = useActiveOrganization();
const logo = org?.organization.data?.logo;

const handleSignup = async () => {
setLoading(true);
Expand Down Expand Up @@ -100,26 +103,23 @@ export default function SignupPage() {
>
<div className="flex h-full w-[53%] shrink-0 items-center justify-between px-7.5">
<div
className="flex h-[94%] w-full flex-col justify-end rounded-[20px] pt-[90%] pb-5 pl-5 pr-[60%]"
className="relative flex h-[94%] w-full flex-col justify-end rounded-[20px] pt-[90%] pb-5 pl-5 pr-[60%]"
style={{
background: `linear-gradient(180deg, ${primary_color} 0%, #FFF 100%)`,
}}
>
<div className="h-32.5 w-84.75">
{logo && (
<img
src={`/images/${logo}`}
alt="Organization Logo"
className="h-26.75 w-26.75"
/>
)}
</div>
<UnauthenticatedOrganizationLogo logoUrl={logoUrl} />
</div>
</div>
<div className="flex h-full flex-1 flex-col items-center justify-between pt-[5%]">
<div className="flex h-full w-[78%] bg-white flex-col items-center gap-[23px] rounded-[30px] border-[2px] border-[#FFF] p-[35px] pt-[12%] shadow-[0_4px_7px_0_rgba(0,0,0,0.4)]">
<h1 className="self-stretch">Sign Up</h1>
<p className="self-stretch text-[20px] text-white">{tagline}</p>
<p
className="self-stretch text-[20px] text-grey-text-strong"
data-testid="organization-tagline"
>
{tagline}
</p>
<BogTextInput
name="first_name"
type="text"
Expand Down
25 changes: 25 additions & 0 deletions components/OrganizationLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { resolveOrganizationLogo } from "@/lib/organizationBranding";
import { cn } from "@/lib/utils";

type OrganizationLogoProps = {
logoUrl?: string | null;
className?: string;
alt?: string;
};

export default function OrganizationLogo({
logoUrl,
className,
alt = "Organization logo",
}: OrganizationLogoProps) {
const { src } = resolveOrganizationLogo(logoUrl);

return (
<img
src={src}
alt={alt}
className={cn("object-contain", className)}
data-testid="organization-logo"
/>
);
}
39 changes: 39 additions & 0 deletions components/UnauthenticatedOrganizationLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import OrganizationLogo from "@/components/OrganizationLogo";
import { resolveOrganizationLogo } from "@/lib/organizationBranding";
import { cn } from "@/lib/utils";

type UnauthenticatedOrganizationLogoProps = {
logoUrl?: string | null;
};

export default function UnauthenticatedOrganizationLogo({
logoUrl,
}: UnauthenticatedOrganizationLogoProps) {
const { isCustom } = resolveOrganizationLogo(logoUrl);

return (
<div className="absolute bottom-8 left-8 flex items-center gap-1">
<OrganizationLogo
logoUrl={logoUrl}
className={cn(
"size-[72px] shrink-0",
!isCustom && "drop-shadow-[0_2px_8px_rgba(253,128,51,0.55)]",
)}
/>
{!isCustom && (
<div className="flex flex-col items-start gap-1">
<img
src="/bog.svg"
alt="bits of good"
className="h-[11.25px] w-[63px]"
/>
<img
src="/sunset.svg"
alt="sunset"
className="h-[20.25px] w-[99px]"
/>
</div>
)}
</div>
);
}
11 changes: 7 additions & 4 deletions components/navigation/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useEffect, useState } from "react";
import { OrganizationConfigKey } from "@/lib/schema";
import useOrganizationConfig from "@/lib/hooks/useOrganizationConfig";
import { cn } from "@/lib/utils";
import OrganizationLogo from "@/components/OrganizationLogo";
import { resolveOrganizationLogo } from "@/lib/organizationBranding";

type SunsetLogoSize = "sm" | "md";

Expand All @@ -27,13 +29,14 @@ export function SunsetLogo({ size = "md", className }: SunsetLogoProps) {
const bogHeightClass = "h-[10px]";
const sunsetHeightClass = "h-[15px]";

const useCustomLogo = mounted && customLogoSrc && customLogoSrc.trim() !== "";
const useCustomLogo =
mounted && resolveOrganizationLogo(customLogoSrc).isCustom;

if (useCustomLogo) {
return (
<div className={cn("flex items-center", className)}>
<img
src={customLogoSrc}
<OrganizationLogo
logoUrl={customLogoSrc}
alt="Organization logo"
className={`${circleClass} w-auto`}
/>
Expand All @@ -43,7 +46,7 @@ export function SunsetLogo({ size = "md", className }: SunsetLogoProps) {

return (
<div className={cn("flex items-center", className)}>
<img src="/logo.svg" alt="Logo" className={`${circleClass} w-auto`} />
<OrganizationLogo alt="Logo" className={`${circleClass} w-auto`} />
<div className="flex flex-col items-start">
<img
src="/bog.svg"
Expand Down
10 changes: 10 additions & 0 deletions lib/organizationBranding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const DEFAULT_ORGANIZATION_LOGO = "/logo.svg";

export function resolveOrganizationLogo(logoUrl?: string | null) {
const customSrc = logoUrl?.trim();

return {
src: customSrc || DEFAULT_ORGANIZATION_LOGO,
isCustom: Boolean(customSrc),
};
}
Loading