-
Notifications
You must be signed in to change notification settings - Fork 16
feat(web): customizable login branding page #1367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e16b451
589b166
df63426
ebbf09b
67f60e6
71e008d
158533a
db92b0e
80aa354
85b1f1e
b6a928e
acfe71f
a2489b0
133946c
3bdbd78
80661cc
c738197
9acf8de
4e8b067
8bb6952
e25bf0a
ff80760
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,10 +275,57 @@ model Session { | |
|
|
||
| // Setup | ||
|
|
||
| type BrandingText { | ||
| en String? | ||
| fr String? | ||
| } | ||
|
|
||
| type ResourceLink { | ||
| href String | ||
| label String | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this not multilingual? |
||
| } | ||
|
|
||
| type BrandingConfig { | ||
| boldDetails Boolean? | ||
| boldName Boolean? | ||
| boldResourceLinks Boolean? | ||
| boldTagline Boolean? | ||
| customLogoHeight Int? | ||
| customLogoSrc String? | ||
| customLogoUrl String? | ||
| customLogoWidth Int? | ||
| customPrimaryColor String? | ||
| customSecondaryColor String? | ||
| detailsFontSize Int? | ||
| instanceDetails BrandingText? | ||
| instanceName BrandingText? | ||
| instanceTagline BrandingText? | ||
| loginTheme String? | ||
| logoAlignment String? | ||
| logoSize String? | ||
| logoSource String? | ||
| nameAlignment String? | ||
| nameFontSize Int? | ||
| panelTextColor String? | ||
| resourceLinks ResourceLink[] | ||
| resourceLinksFontSize Int? | ||
| rightPanelPrimaryColor String? | ||
| rightPanelSecondaryColor String? | ||
| rightPanelTheme String? | ||
| sectionsOrder String[] | ||
| showDetails Boolean? | ||
| showFooterLinks Boolean? | ||
| showLogo Boolean? | ||
| showResourceLinks Boolean? | ||
| showTagline Boolean? | ||
| taglineFontSize Int? | ||
| } | ||
|
|
||
| model SetupState { | ||
| createdAt DateTime @default(now()) @db.Date | ||
| updatedAt DateTime @updatedAt @db.Date | ||
| id String @id @default(auto()) @map("_id") @db.ObjectId | ||
| createdAt DateTime @default(now()) @db.Date | ||
| updatedAt DateTime @updatedAt @db.Date | ||
| id String @id @default(auto()) @map("_id") @db.ObjectId | ||
| branding BrandingConfig? | ||
| isDemo Boolean | ||
| isExperimentalFeaturesEnabled Boolean? | ||
| isSetup Boolean | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| import { ValidationSchema } from '@douglasneuroinformatics/libnest'; | ||
| import { ApiProperty } from '@nestjs/swagger'; | ||
| import { $UpdateSetupStateData } from '@opendatacapture/schemas/setup'; | ||
| import type { UpdateSetupStateData } from '@opendatacapture/schemas/setup'; | ||
| import type { BrandingConfig, UpdateSetupStateData } from '@opendatacapture/schemas/setup'; | ||
|
|
||
| @ValidationSchema($UpdateSetupStateData) | ||
| export class UpdateSetupStateDto implements UpdateSetupStateData { | ||
| @ApiProperty() | ||
| @ApiProperty({ required: false }) | ||
| branding?: BrandingConfig | null; | ||
|
|
||
| @ApiProperty({ required: false }) | ||
| isExperimentalFeaturesEnabled?: boolean; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,11 @@ import { useTranslation } from '@douglasneuroinformatics/libui/hooks'; | |
| import { Link } from '@tanstack/react-router'; | ||
|
|
||
| import { config } from '@/config'; | ||
|
|
||
| const CURRENT_YEAR = new Date().getFullYear(); | ||
| import { useCurrentYear } from '@/hooks/useCurrentYear'; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this hook is completely over-engineered. please remove completely or justify why this is necessary |
||
|
|
||
| export const Footer = () => { | ||
| const { t } = useTranslation('layout'); | ||
| const currentYear = useCurrentYear(); | ||
|
|
||
| return ( | ||
| <footer className="text-muted-foreground container py-3 text-sm" data-testid="footer"> | ||
|
|
@@ -51,7 +51,7 @@ export const Footer = () => { | |
| </div> | ||
| </div> | ||
| <p className="text-center"> | ||
| © {CURRENT_YEAR} {t('organization.name')} | ||
| © {currentYear} {t('organization.name')} | ||
| </p> | ||
| </footer> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,14 +6,28 @@ import { Sidebar } from '../Sidebar'; | |
|
|
||
| export const Layout = () => { | ||
| return ( | ||
| <div className="flex h-screen w-screen flex-col md:flex-row" data-testid="layout"> | ||
| // `w-full` (100% of #root, which excludes the scrollbar gutter) rather than | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This breaks different parts of ODC layout. Please revert |
||
| // `w-screen` (100vw, which *includes* it): when a vertical scrollbar is | ||
| // present, 100vw is wider than the document and pushes the body sideways, | ||
| // exposing the slate body background. `overflow-clip` additionally prevents | ||
| // any page or child from extending the layout below the footer. | ||
| <div className="flex h-screen w-full flex-col overflow-clip md:flex-row" data-testid="layout"> | ||
| <div className="absolute md:hidden"> | ||
| <Navbar /> | ||
| </div> | ||
| <div className="hidden md:flex md:shrink-0"> | ||
| <Sidebar /> | ||
| </div> | ||
| <div className="scrollbar-none flex grow flex-col overflow-y-scroll pt-14 md:pt-0" data-testid="layout-main"> | ||
| {/* | ||
| The main scroll region scrolls vertically. `overflow-x-clip` joins | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all of this should be reverted. the scroll layout on your page is still broken even with these changes |
||
| `overflow-y-scroll` so the region scrolls only along Y while still | ||
| clipping any horizontal overflow without breaking `position: sticky` | ||
| inside (clip does not create a separate scroll container). | ||
| */} | ||
| <div | ||
| className="scrollbar-none flex grow flex-col overflow-x-clip overflow-y-scroll pt-14 md:pt-0" | ||
| data-testid="layout-main" | ||
| > | ||
| <main className="container flex grow flex-col"> | ||
| <Outlet /> | ||
| </main> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import type { BrandingConfig } from '@opendatacapture/schemas/setup'; | ||
| import type { Meta, StoryObj } from '@storybook/react-vite'; | ||
|
|
||
| import { LoginBrandingPanel } from './LoginBrandingPanel'; | ||
|
|
||
| type Story = StoryObj<typeof LoginBrandingPanel>; | ||
|
|
||
| const baseBranding: BrandingConfig = { | ||
| instanceName: { en: 'Open Data Capture', fr: 'Open Data Capture' }, | ||
| instanceTagline: { | ||
| en: 'A platform for clinical and research data collection.', | ||
| fr: 'Une plateforme pour la collecte de données cliniques et de recherche.' | ||
| }, | ||
| loginTheme: 'ocean' | ||
| }; | ||
|
|
||
| export const Default: Story = { | ||
| args: { | ||
| branding: baseBranding, | ||
| className: 'h-screen w-screen' | ||
| } | ||
| }; | ||
|
|
||
| export const Preview: Story = { | ||
| args: { | ||
| branding: baseBranding, | ||
| className: 'h-96 w-[36rem]', | ||
| preview: true | ||
| } | ||
| }; | ||
|
|
||
| export const WithResources: Story = { | ||
| args: { | ||
| branding: { | ||
| ...baseBranding, | ||
| loginTheme: 'midnight', | ||
| resourceLinks: [ | ||
| { href: 'https://example.org/handbook', label: 'Handbook' }, | ||
| { href: 'https://example.org/contact', label: 'Contact' } | ||
| ], | ||
| showResourceLinks: true | ||
| }, | ||
| className: 'h-screen w-screen' | ||
| } | ||
| }; | ||
|
|
||
| export const CustomGradient: Story = { | ||
| args: { | ||
| branding: { | ||
| ...baseBranding, | ||
| customPrimaryColor: '#0ea5e9', | ||
| customSecondaryColor: '#7c3aed', | ||
| loginTheme: 'custom' | ||
| }, | ||
| className: 'h-screen w-screen' | ||
| } | ||
| }; | ||
|
|
||
| export default { component: LoginBrandingPanel } as Meta<typeof LoginBrandingPanel>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please revert all changes in this file