Skip to content

Commit

Permalink
Merge pull request #703 from contember/feat/headless-tenant
Browse files Browse the repository at this point in the history
new tenant components
  • Loading branch information
matej21 authored May 29, 2024
2 parents 43d0e6e + 64957d8 commit 4c069bf
Show file tree
Hide file tree
Showing 140 changed files with 5,485 additions and 316 deletions.
1,475 changes: 1,473 additions & 2 deletions build/api/react-client-tenant.api.md

Large diffs are not rendered by default.

88 changes: 5 additions & 83 deletions build/api/react-identity.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
```ts

import { Environment } from '@contember/react-binding';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { Identity } from '@contember/react-client-tenant';
import { NamedExoticComponent } from 'react';
import { ReactNode } from 'react';

Expand All @@ -20,79 +20,18 @@ export interface HasRoleProps {
role: RoleCondition;
}

// @public (undocumented)
export interface Identity {
// (undocumented)
readonly id: string;
// (undocumented)
readonly permissions: {
readonly canCreateProject: boolean;
};
// (undocumented)
readonly person?: Person;
// (undocumented)
readonly projects: IdentityProject[];
}

// @public (undocumented)
export const identityEnvironmentExtension: Environment.Extension<Identity | null, {
identity: Identity | undefined;
}>;

// @public (undocumented)
export interface IdentityMethods {
// (undocumented)
clearIdentity: () => void;
// (undocumented)
refreshIdentity: () => Promise<void>;
}

// @public (undocumented)
export interface IdentityProject {
// (undocumented)
readonly name: string;
// (undocumented)
readonly roles: readonly string[];
// (undocumented)
readonly slug: string;
}

// @public (undocumented)
export const IdentityProvider: React.FC<IdentityProviderProps>;

// @public (undocumented)
export interface IdentityProviderProps {
// (undocumented)
children: ReactNode;
}

// @public (undocumented)
export const IdentityState: ({ state, children }: IdentityStateProps) => JSX_2.Element | null;
export const IdentityEnvironmentProvider: React.FC<IdentityEnvironmentProviderProps>;

// @public (undocumented)
export interface IdentityStateProps {
// (undocumented)
children: ReactNode;
export interface IdentityEnvironmentProviderProps {
// (undocumented)
state: IdentityStateValue | IdentityStateValue[];
}

// @public (undocumented)
export type IdentityStateValue = 'none' | 'loading' | 'failed' | 'cleared' | 'success';

// @public (undocumented)
export const LogoutTrigger: ({ children }: {
children: ReactNode;
}) => JSX_2.Element;

// @public (undocumented)
export interface Person {
// (undocumented)
readonly email?: string;
// (undocumented)
readonly id: string;
// (undocumented)
readonly otpEnabled: boolean;
}

// @public (undocumented)
Expand All @@ -107,27 +46,10 @@ export type ProjectUserRoles = Set<string>;
export type RoleCondition = string | ((roles: Set<string>) => boolean);

// @public (undocumented)
export const useFetchIdentity: () => [{
state: IdentityStateValue;
identity: Identity | undefined;
}, IdentityMethods];

// @public (undocumented)
export const useIdentity: () => Identity | undefined;

// @public (undocumented)
export const useIdentityMethods: () => IdentityMethods;

// @public (undocumented)
export const useIdentityState: () => IdentityStateValue;
export const useProjectUserRoles: () => ProjectUserRoles;

// @public (undocumented)
export const useLogout: () => ({ noRedirect }?: {
noRedirect?: boolean | undefined;
}) => Promise<void>;

// @public (undocumented)
export const useProjectUserRoles: () => ProjectUserRoles;
export * from "@contember/react-client-tenant";

// (No @packageDocumentation comment for this package)

Expand Down
8 changes: 8 additions & 0 deletions build/api/react-routing.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ export class RoutingParameter {
// @public (undocumented)
export type RoutingParameterResolver = (name: string) => RequestParameterValue | undefined;

// @public (undocumented)
export const RoutingProvider: ({ children, ...props }: RoutingProviderProps) => JSX_2.Element;

// @public (undocumented)
export type RoutingProviderProps = Partial<RoutingContextValue> & {
children: ReactNode;
};

// @public (undocumented)
export interface SelectedDimension {
// (undocumented)
Expand Down
1 change: 0 additions & 1 deletion contember.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
projects:
admin-sandbox: packages/admin-sandbox
playground: packages/playground
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ services:
CONTEMBER_SKIP_VERSION_CHECK: 1
CONTEMBER_API_URL: 'http://contember-engine:4000/'
CONTEMBER_API_TOKEN: '0000000000000000000000000000000000000000'
CONTEMBER_PROJECT_NAME: playground

volumes:
- ./:/src
Expand Down
34 changes: 18 additions & 16 deletions packages/admin/src/components/Identity/IdentityProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Message, SpinnerOverlay } from '@contember/ui'
import { ReactNode, useEffect } from 'react'
import { MiscPageLayout } from '../MiscPageLayout'
import { InvalidIdentityFallback } from './InvalidIdentityFallback'
import { IdentityProvider as BaseIdentityProvider, IdentityState } from '@contember/react-identity'
import { IdentityProvider as BaseIdentityProvider, IdentityEnvironmentProvider, IdentityState } from '@contember/react-identity'

export interface IdentityProviderProps {
children: ReactNode
Expand All @@ -28,21 +28,23 @@ const ClearIdentityHandler = ({ onInvalidIdentity }: {
export const IdentityProvider: React.FC<IdentityProviderProps> = ({ children, onInvalidIdentity, allowUnauthenticated }) => {
return (
<BaseIdentityProvider>
<IdentityState state={'cleared'}>
<ClearIdentityHandler onInvalidIdentity={onInvalidIdentity}/>
<MiscPageLayout>
<Message size="large" padding="large" display="block">Logging out&hellip;</Message>
</MiscPageLayout>
</IdentityState>
<IdentityState state={'failed'}>
<InvalidIdentityFallback />
</IdentityState>
<IdentityState state={allowUnauthenticated ? 'loading' : ['loading', 'none']}>
<SpinnerOverlay />
</IdentityState>
<IdentityState state={allowUnauthenticated ? ['success', 'none'] : 'success'}>
{children}
</IdentityState>
<IdentityEnvironmentProvider>
<IdentityState state={'cleared'}>
<ClearIdentityHandler onInvalidIdentity={onInvalidIdentity}/>
<MiscPageLayout>
<Message size="large" padding="large" display="block">Logging out&hellip;</Message>
</MiscPageLayout>
</IdentityState>
<IdentityState state={'failed'}>
<InvalidIdentityFallback />
</IdentityState>
<IdentityState state={allowUnauthenticated ? 'loading' : ['loading', 'none']}>
<SpinnerOverlay />
</IdentityState>
<IdentityState state={allowUnauthenticated ? ['success', 'none'] : 'success'}>
{children}
</IdentityState>
</IdentityEnvironmentProvider>
</BaseIdentityProvider>
)
}
40 changes: 20 additions & 20 deletions packages/interface/src/bootstrap/ApplicationEntrypoint.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Environment, EnvironmentContext, EnvironmentExtensionProvider } from '@contember/react-binding'
import { ContemberClient, ContemberClientProps } from '@contember/react-client'
import { ReactNode } from 'react'
import { RequestProvider, RouteMap, RoutingContext, RoutingContextValue, SelectedDimension } from '@contember/react-routing'
import { RequestProvider, RouteMap, RoutingContext, RoutingContextValue, RoutingProvider, SelectedDimension } from '@contember/react-routing'
import { DataViewPageNameKeyProvider } from './DataViewPageNameKeyProvider'
import { IdentityProvider, projectEnvironmentExtension } from '@contember/react-identity'
import { IdentityEnvironmentProvider, IdentityProvider, projectEnvironmentExtension } from '@contember/react-identity'

export interface ApplicationEntrypointProps extends ContemberClientProps {
basePath: string
Expand Down Expand Up @@ -36,25 +36,25 @@ export const ApplicationEntrypoint = (props: ApplicationEntrypointProps) => {

return (
<EnvironmentContext.Provider value={rootEnv}>
<RoutingContext.Provider value={routing}>
<RequestProvider>
<ContemberClient
apiBaseUrl={props.apiBaseUrl}
sessionToken={props.sessionToken}
loginToken={props.loginToken}
project={props.project}
stage={props.stage}
>
<EnvironmentExtensionProvider extension={projectEnvironmentExtension} state={props.project ?? null}>
<DataViewPageNameKeyProvider>
<IdentityProvider>
<RoutingProvider {...routing}>
<ContemberClient
apiBaseUrl={props.apiBaseUrl}
sessionToken={props.sessionToken}
loginToken={props.loginToken}
project={props.project}
stage={props.stage}
>
<EnvironmentExtensionProvider extension={projectEnvironmentExtension} state={props.project ?? null}>
<DataViewPageNameKeyProvider>
<IdentityProvider>
<IdentityEnvironmentProvider>
{props.children}
</IdentityProvider>
</DataViewPageNameKeyProvider>
</EnvironmentExtensionProvider>
</ContemberClient>
</RequestProvider>
</RoutingContext.Provider>
</IdentityEnvironmentProvider>
</IdentityProvider>
</DataViewPageNameKeyProvider>
</EnvironmentExtensionProvider>
</ContemberClient>
</RoutingProvider>
</EnvironmentContext.Provider>
)
}
1 change: 1 addition & 0 deletions packages/playground/admin/.env.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
VITE_CONTEMBER_ADMIN_API_BASE_URL=http://localhost:3001
VITE_CONTEMBER_ADMIN_SESSION_TOKEN=0000000000000000000000000000000000000000
VITE_CONTEMBER_ADMIN_LOGIN_TOKEN=1111111111111111111111111111111111111111
VITE_CONTEMBER_ADMIN_PROJECT_NAME=playground
1 change: 1 addition & 0 deletions packages/playground/admin/.env.production
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
VITE_CONTEMBER_ADMIN_API_BASE_URL=/_api
VITE_CONTEMBER_ADMIN_SESSION_TOKEN=__SESSION_TOKEN__
VITE_CONTEMBER_ADMIN_LOGIN_TOKEN=__LOGIN_TOKEN__
7 changes: 6 additions & 1 deletion packages/playground/admin/app/components/navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArchiveIcon, BrushIcon, FormInputIcon, GripVertical, HomeIcon, KanbanIcon, LanguagesIcon, PencilIcon, TableIcon, UploadIcon } from 'lucide-react'
import { ArchiveIcon, BrushIcon, FormInputIcon, GripVertical, HomeIcon, KanbanIcon, KeyRoundIcon, LanguagesIcon, LockKeyholeIcon, PencilIcon, TableIcon, UploadIcon, UserIcon, UsersIcon } from 'lucide-react'
import { Menu, MenuItem } from '../../lib/components/ui/menu'


Expand All @@ -8,6 +8,11 @@ export const Navigation = () => {
<div>
<Menu>
<MenuItem icon={<HomeIcon size={16} />} label={'Home'} to={'index'} />
<MenuItem icon={<UserIcon size={16} />} label={'Tenant'}>
<MenuItem icon={<LockKeyholeIcon />} label={'Security'} to={'tenant/security'} />
<MenuItem icon={<UsersIcon />} label={'Members'} to={'tenant/members'} />
<MenuItem icon={<KeyRoundIcon />} label={'API keys'} to={'tenant/apiKeys'} />
</MenuItem>
<MenuItem icon={<BrushIcon size={16} />} label={'UI'}>
<MenuItem icon={line} label={'Buttons'} to={'ui/button'} />
<MenuItem icon={line} label={'Toasts'} to={'ui/toast'} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
export const getConfig = () => {

let project = import.meta.env.VITE_CONTEMBER_ADMIN_PROJECT_NAME

if (project === '__PROJECT_SLUG__') {
project = window.location.pathname.split('/')[1]
}

let basePath = import.meta.env.BASE_URL ?? '/'
if (basePath === './') {
basePath = `/${project}/`
}
const basePath = `/${window.location.pathname.split('/')[1]}/`

const apiBaseUrl = import.meta.env.VITE_CONTEMBER_ADMIN_API_BASE_URL as string
if (!apiBaseUrl) {
throw new Error('VITE_CONTEMBER_ADMIN_API_BASE_URL is not set')
Expand Down
14 changes: 14 additions & 0 deletions packages/playground/admin/app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, viewport-fit=cover, initial-scale=1">
<link rel="icon" type="image/png" href="../favicon.png">
<link rel="apple-touch-icon" href="../apple-touch-icon.png">
<title>Contember Interface</title>
<script type="module" src="/app/index.tsx"></script>

</head>

</html>
40 changes: 40 additions & 0 deletions packages/playground/admin/app/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApplicationEntrypoint, PageModule, Pages } from '@contember/interface'
import { SlotsProvider } from '@contember/react-slots'
import { Layout } from './components/layout'
import '../index.css'
import { Toaster } from '../lib/components/ui/toast'
import { createErrorHandler, DevBar, DevPanel } from '@contember/react-devbar'
import { LogInIcon } from 'lucide-react'
import { LoginWithEmail } from '../lib/components/dev/login-panel'
import { createRoot } from 'react-dom/client'
import { getConfig } from './config'
import { OutdatedApplicationDialog } from '../lib/components/outdated-application-dialog'

const errorHandler = createErrorHandler((dom, react, onRecoverableError) => createRoot(dom, { onRecoverableError }).render(react))

const rootEl = document.body.appendChild(document.createElement('div'))

errorHandler(onRecoverableError => createRoot(rootEl, { onRecoverableError }).render(<>
<SlotsProvider>
<ApplicationEntrypoint
{...getConfig()}
children={
<>
<Toaster>
<Pages
layout={Layout}
children={import.meta.glob<PageModule>(
'./pages/**/*.tsx',
{ eager: true },
)}
/>
{import.meta.env.DEV && <DevBar>
<DevPanel heading="Login" icon={<LogInIcon />}><LoginWithEmail /></DevPanel>
</DevBar>}
</Toaster>
<OutdatedApplicationDialog />
</>
}
/>
</SlotsProvider>
</>))
Loading

0 comments on commit 4c069bf

Please sign in to comment.