Merge branch 'dev' into staging

This commit is contained in:
Vins 2024-04-04 15:14:05 +02:00
commit d53057ee2f
24 changed files with 767 additions and 45 deletions

41
package-lock.json generated
View File

@ -162,9 +162,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
"version": "7.24.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
"integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@ -474,23 +474,23 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz",
"integrity": "sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==",
"version": "5.15.15",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
"integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
}
},
"node_modules/@mui/material": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.14.tgz",
"integrity": "sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==",
"version": "5.15.15",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
"integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/base": "5.0.0-beta.40",
"@mui/core-downloads-tracker": "^5.15.14",
"@mui/system": "^5.15.14",
"@mui/core-downloads-tracker": "^5.15.15",
"@mui/system": "^5.15.15",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"@types/react-transition-group": "^4.4.10",
@ -584,9 +584,9 @@
}
},
"node_modules/@mui/system": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.14.tgz",
"integrity": "sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==",
"version": "5.15.15",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
"integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/private-theming": "^5.15.14",
@ -1391,13 +1391,12 @@
"optional": true
},
"node_modules/bare-fs": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.2.tgz",
"integrity": "sha512-X9IqgvyB0/VA5OZJyb5ZstoN62AzD7YxVGog13kkfYWYqJYcK0kcqLZ6TrmH5qr4/8//ejVcX4x/a0UvaogXmA==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.3.tgz",
"integrity": "sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==",
"optional": true,
"dependencies": {
"bare-events": "^2.0.0",
"bare-os": "^2.0.0",
"bare-path": "^2.0.0",
"streamx": "^2.13.0"
}
@ -4481,9 +4480,9 @@
}
},
"node_modules/sass": {
"version": "1.72.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz",
"integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==",
"version": "1.74.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz",
"integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",

View File

@ -10,6 +10,10 @@ export type IPostStripeResponse = {
url: string;
};
export type IGetClientPortalSessionResponse = {
url: string;
};
export default class Stripe extends BaseAdmin {
private static instance: Stripe;
private readonly baseURl = this.namespaceUrl.concat("/stripe");
@ -37,4 +41,14 @@ export default class Stripe extends BaseAdmin {
return Promise.reject(err);
}
}
public async getClientPortalSession(stripe_subscription_id: string) {
const url = new URL(this.baseURl.concat(`/${stripe_subscription_id}`));
try {
return await this.getRequest<IGetClientPortalSessionResponse>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -0,0 +1,48 @@
import { Subscription } from "le-coffre-resources/dist/Admin";
import BaseAdmin from "../../../../../common/Api/LeCoffreApi/Admin/BaseAdmin";
export interface IPostSubscriptionsParams {
emails: string[];
}
export default class Subscriptions extends BaseAdmin {
private static instance: Subscriptions;
private readonly baseURl = this.namespaceUrl.concat("/subscriptions");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new this();
} else {
return this.instance;
}
}
public async get(q: any) {
const url = new URL(this.baseURl);
const query = { q };
if (q) Object.entries(query).forEach(([key, value]) => url.searchParams.set(key, JSON.stringify(value)));
try {
return await this.getRequest<Subscription>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
/**
* @description : Create a Document
*/
public async post(body: IPostSubscriptionsParams) {
const url = new URL(this.baseURl.concat(`/invite`));
try {
return await this.postRequest(url, body as any);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -40,11 +40,11 @@ export default function Navigation() {
read: false,
},
include: {
notification: true
notification: true,
},
orderBy: {
notification: {created_at: "desc"},
}
notification: { created_at: "desc" },
},
});
notifications.forEach((notification) => {
Toasts.getInstance().open({
@ -89,6 +89,19 @@ export default function Navigation() {
routesActive={[Module.getInstance().get().modules.pages.Collaborators.props.path]}
/>
</Rules>
<HeaderLink
text={"Abonnement"}
path={Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path}
routesActive={[
Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Invite.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Success.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Error.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.New.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.pages.Standard.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.pages.Illimity.props.path,
]}
/>
</div>
);
}

View File

@ -4,6 +4,7 @@ import classNames from "classnames";
import React, { ReactNode } from "react";
import classes from "./classes.module.scss";
import BackArrow from "@Front/Components/Elements/BackArrow";
type IProps = {
title: string;
@ -14,6 +15,8 @@ type IProps = {
scrollTop: number | null;
isPadding?: boolean;
hasHeaderLinks: boolean;
hasBackArrow?: boolean;
backArrowUrl?: string;
};
type IState = {};
@ -28,7 +31,14 @@ export default class DefaultTemplate extends React.Component<IProps, IState> {
return (
<>
<Header isUserConnected={this.props.hasHeaderLinks} />
<div className={classNames(classes["root"], this.props.isPadding && classes["padding"])}>{this.props.children}</div>
<div className={classNames(classes["root"], this.props.isPadding && classes["padding"])}>
{this.props.hasBackArrow && (
<div className={classes["back-arrow-desktop"]}>
<BackArrow url={this.props.backArrowUrl ?? ""} />
</div>
)}
{this.props.children}
</div>
<Version />
</>
);

View File

@ -46,8 +46,6 @@ export default function SubscribeCheckoutTicket(props: IProps) {
};
const handleSubmitPayment = async () => {
console.log("handleSubmitPayment");
const stripeCheckout = {
type: EType.Standard,
nb_seats: numberOfCollaborators,

View File

@ -31,7 +31,7 @@ export default function SubscribeIllimity() {
return (
<>
<DefaultTemplate title="Nouvelle souscription" hasHeaderLinks={false}>
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
<div className={classes["root"]}>
<div className={classes["left"]}>
<NavTab

View File

@ -38,7 +38,7 @@ export default function SubscribeStandard() {
return (
<>
<DefaultTemplate title="Nouvelle souscription" hasHeaderLinks={false}>
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
<div className={classes["root"]}>
<div className={classes["left"]}>
<NavTab

View File

@ -8,7 +8,7 @@ import Button from "@Front/Components/DesignSystem/Button";
export default function SubscriptionError() {
return (
<DefaultTemplate title="Erreur à la souscription" hasHeaderLinks={false}>
<DefaultTemplate title="Erreur à la souscription">
<div className={classes["root"]}>
<div className={classes["left"]}>
<div className={classes["title"]}>

View File

@ -33,6 +33,7 @@
display: flex;
flex-direction: column;
gap: 32px;
height: fit-content;
&[data-inactive="true"] {
border: 1px solid #e7e7e7;
@ -62,6 +63,12 @@
flex-direction: column;
gap: 8px;
}
.button-container {
display: flex;
flex-direction: column;
gap: 8px;
}
}
}

View File

@ -6,12 +6,19 @@ import { useCallback, useState } from "react";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import useOpenable from "@Front/Hooks/useOpenable";
import MessageBox from "@Front/Components/Elements/MessageBox";
import Link from "next/link";
import Module from "@Front/Config/Module";
import Subscriptions from "@Front/Api/LeCoffreApi/Admin/Subscriptions/Subscriptions";
import JwtService from "@Front/Services/JwtService/JwtService";
import Stripe from "@Front/Api/LeCoffreApi/Admin/Stripe/Stripe";
import { useRouter } from "next/router";
export enum EForfeitType {
"standard",
"unlimited",
}
export default function SubscriptionFacturation() {
const router = useRouter();
const [forfeitType, _setForfeitType] = useState(EForfeitType.standard);
const { close: closeCancelSubscription, isOpen: isCancelSubscriptionOpen, open: openCancelSubscription } = useOpenable();
const { close: closeConfirmation, isOpen: isConfirmationOpen, open: openConfirmation } = useOpenable();
@ -22,13 +29,22 @@ export default function SubscriptionFacturation() {
return;
}, [closeCancelSubscription, openConfirmation]);
const manageBilling = async () => {
try {
const jwt = JwtService.getInstance().decodeJwt();
const subscription = await Subscriptions.getInstance().get({ officeId: jwt?.office_Id });
const stripe_client_portal = await Stripe.getInstance().getClientPortalSession(subscription.stripe_subscription_id!);
router.push(stripe_client_portal.url);
} catch (error) {}
};
return (
<DefaultTemplate title="Nouvelle souscription" hasHeaderLinks={false}>
<DefaultTemplate title="Nouvelle souscription">
<div className={classes["root"]}>
<div className={classes["top-container"]}>
<div className={classes["top-container-title"]}>
<Typography typo={ITypo.H1} color={ITypoColor.BLACK}>
Facturation
Abonnement
</Typography>
</div>
<div className={classes["sub-title"]}>
@ -71,14 +87,29 @@ export default function SubscriptionFacturation() {
</div>
<div className={classes["button-container"]}>
{forfeitType !== EForfeitType.standard && (
<Link
href={Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.pages.Standard.props.path}>
<Button fullwidth variant={EButtonVariant.GHOST}>
Rétrograder mon abonnement
</Button>
</Link>
)}
{forfeitType === EForfeitType.standard && (
<>
<Link
href={
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.pages.Standard.props.path
}>
<Button fullwidth variant={EButtonVariant.PRIMARY}>
Gérer mes collaborateurs
Gérer mon abonnement
</Button>
</Link>
<Link href={Module.getInstance().get().modules.pages.Subscription.pages.ManageCollaborators.props.path}>
<Button fullwidth variant={EButtonVariant.GHOST}>
Ajouter des collaborateurs
</Button>
</Link>
</>
)}
</div>
</div>
@ -120,9 +151,12 @@ export default function SubscriptionFacturation() {
</Button>
)}
{forfeitType === EForfeitType.standard && (
<Link
href={Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.pages.Illimity.props.path}>
<Button fullwidth variant={EButtonVariant.GHOST}>
Améliorer mon abonnement
</Button>
</Link>
)}
</div>
</div>
@ -133,7 +167,7 @@ export default function SubscriptionFacturation() {
Arrêter l'abonnement
</Typography>
</Button>
<Button>Gérer la facturation</Button>
<Button onClick={manageBilling}>Gérer la facturation</Button>
</div>
</div>
<Confirm

View File

@ -0,0 +1,38 @@
.root {
max-width: 1400px;
margin: auto;
.container {
display: flex;
text-align: center;
justify-content: center;
flex-direction: column;
gap: 32px;
.emails-form {
display: flex;
flex-direction: column;
gap: 32px;
.input-container {
display: flex;
gap: 16px;
justify-content: flex-start;
align-items: center;
> span {
width: 100%;
flex: 1;
}
}
.add-line-container {
display: flex;
justify-content: flex-start;
}
.button-container {
display: flex;
justify-content: center;
}
}
}
}

View File

@ -0,0 +1,126 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import classes from "./classes.module.scss";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import { useRouter } from "next/router";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form";
import Subscriptions from "@Front/Api/LeCoffreApi/Admin/Subscriptions/Subscriptions";
import { useCallback, useState } from "react";
import { TrashIcon } from "@heroicons/react/24/outline";
import PlusIcon from "@Assets/Icons/plus.svg";
export enum EForfeitType {
"standard",
"unlimited",
}
type EmailLine = {
element: JSX.Element;
id: number;
};
export default function SubscriptionInvite() {
const router = useRouter();
const nbOfCollaborators = parseInt(router.query["nbOfCollaborators"] as string);
const [incrementalId, setIncrementalId] = useState(isNaN(nbOfCollaborators) ? 0 : nbOfCollaborators);
const getInitialLines = () => {
const linesToGet = isNaN(nbOfCollaborators) ? 1 : nbOfCollaborators;
const lines: EmailLine[] = [];
for (let i = 0; i < linesToGet; i++) {
lines.push({
element: <TextField key={i} name={`email_${i}`} placeholder="Email" className={classes["input"]} required />,
id: i,
});
}
return lines;
};
const [lines, setLines] = useState<EmailLine[]>(getInitialLines());
const sendInvitations = async (e: React.FormEvent<HTMLFormElement> | null) => {
if (!e) return;
e.preventDefault();
const form = e.target as HTMLFormElement;
const emails: string[] = [];
Object.keys(form.elements).forEach((key) => {
if (isNaN(parseInt(key))) return;
const element = form.elements[key as any] as HTMLInputElement;
if (element.name.includes("email_")) {
emails.push(element.value);
}
});
try {
await Subscriptions.getInstance().post({
emails,
});
} catch (e) {
console.error(e);
}
};
const addLine = useCallback(() => {
const newLine: EmailLine = {
element: (
<TextField key={lines.length} name={`email_${lines.length}`} placeholder="Email" className={classes["input"]} required />
),
id: incrementalId + 1,
};
setIncrementalId(incrementalId + 1);
setLines((prev) => [...prev, newLine]);
}, [incrementalId, lines]);
const deleteLine = (e: React.MouseEvent<SVGSVGElement>) => {
const lineId = parseInt(e.currentTarget.getAttribute("data-line") as string);
setLines((prev) => prev.filter((line) => line.id !== lineId));
};
return (
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
<div className={classes["root"]}>
<div className={classes["container"]}>
<Typography typo={ITypo.H1} color={ITypoColor.BLACK}>
Inviter vos collaborateurs
</Typography>
{!isNaN(nbOfCollaborators) && (
<Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
{nbOfCollaborators} collaborateurs à inviter
</Typography>
)}
{isNaN(nbOfCollaborators) && (
<Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
Collaborateurs illimités
</Typography>
)}
<Form className={classes["emails-form"]} onSubmit={sendInvitations}>
{lines.map((line, index) => (
<div key={line.id} className={classes["input-container"]}>
{line.element}
{!nbOfCollaborators && (
<TrashIcon
width="20"
height="20"
data-line={line.id}
onClick={deleteLine}
visibility={index === 0 ? "hidden" : "visible"}
/>
)}
</div>
))}
{isNaN(nbOfCollaborators) && (
<div className={classes["add-line-container"]}>
<Button onClick={addLine} variant={EButtonVariant.LINE} icon={PlusIcon}>
Ajouter une adresse email
</Button>
</div>
)}
<div className={classes["button-container"]}>
<Button type="submit">Envoyer l'invitation</Button>
</div>
</Form>
</div>
</div>
</DefaultTemplate>
);
}

View File

@ -0,0 +1,30 @@
@import "@Themes/constants.scss";
.root {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 16px;
form {
display: flex;
flex-direction: column;
gap: 16px;
.collaborators-container {
display: flex;
flex-direction: column;
gap: 32px;
margin-top: 24px;
}
.buttons-container {
display: flex;
gap: 32px;
max-width: 400px;
@media (max-width: $screen-s) {
flex-direction: column;
gap: 16px;
}
}
}
}

View File

@ -0,0 +1,95 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import classes from "./classes.module.scss";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Form from "@Front/Components/DesignSystem/Form";
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
export default function SubscriptionManageCollaborators() {
return (
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
<div className={classes["root"]}>
<Typography typo={ITypo.H2} color={ITypoColor.BLACK}>
Choisissez les collaborateurs pour votre abonnement
</Typography>
<Typography typo={ITypo.P_SB_18} color={ITypoColor.BLACK}>
7 sièges disponibles
</Typography>
<Form>
<div className={classes["collaborators-container"]}>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
checked
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
checked
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
checked
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
/>
<CheckBox
option={{
label: "Jean Dupont",
value: "Jean Dupont",
}}
name="collaborators"
/>
</div>
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.BLACK}>
7 collaborateurs sélectionnés
</Typography>
<div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.PRIMARY} fullwidth>
Enregistrer
</Button>
<Button variant={EButtonVariant.GHOST} fullwidth>
Annuler
</Button>
</div>
</Form>
</div>
</DefaultTemplate>
);
}

View File

@ -8,7 +8,7 @@ import Link from "next/link";
export default function SubscriptionNew() {
return (
<DefaultTemplate title="Nouvelle souscription" hasHeaderLinks={false}>
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
<div className={classes["root"]}>
<div className={classes["top-container"]}>
<div className={classes["top-container-title"]}>

View File

@ -5,10 +5,12 @@ import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Ty
import MessageBox from "@Front/Components/Elements/MessageBox";
import SubscriptionClientInfos from "../SubscriptionClientInfos";
import Button from "@Front/Components/DesignSystem/Button";
import Link from "next/link";
import Module from "@Front/Config/Module";
export default function SubscriptionSuccess() {
return (
<DefaultTemplate title="Abonnement réussi" hasHeaderLinks={false}>
<DefaultTemplate title="Abonnement réussi">
<div className={classes["root"]}>
<div className={classes["left"]}>
<div className={classes["title"]}>
@ -29,7 +31,9 @@ export default function SubscriptionSuccess() {
<SubscriptionClientInfos />
</div>
<div className={classes["separator"]} />
<Link href={Module.getInstance().get().modules.pages.Subscription.pages.Invite.props.path}>
<Button>Inviter vos collaborateurs</Button>
</Link>
</div>
<div className={classes["right"]}>
<SubscriptionTicket />

View File

@ -270,6 +270,80 @@
"path": "/404",
"labelKey": "not_found"
}
},
"Subscription": {
"enabled": true,
"props": {
"path": "/subscription",
"labelKey": "subscription"
},
"pages": {
"Invite": {
"enabled": true,
"props": {
"path": "/subscription/invite",
"labelKey": "invite"
}
},
"ManageCollaborators": {
"enabled": true,
"props": {
"path": "/subscription/manage-collaborators",
"labelKey": "manage_collaborators"
}
},
"Manage": {
"enabled": true,
"props": {
"path": "/subscription/manage",
"labelKey": "manage"
}
},
"New": {
"enabled": true,
"props": {
"path": "/subscription/new",
"labelKey": "subscribe"
}
},
"Error": {
"enabled": true,
"props": {
"path": "/subscription/error",
"labelKey": "error"
}
},
"Success": {
"enabled": true,
"props": {
"path": "/subscription/success",
"labelKey": "success"
}
},
"Subscribe": {
"enabled": true,
"props": {
"path": "/subscription/subscribe",
"labelKey": "subscribe"
},
"pages": {
"Standard": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/standard",
"labelKey": "standard"
}
},
"Illimity": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/illimity",
"labelKey": "illimity"
}
}
}
}
}
}
}
}

View File

@ -270,6 +270,80 @@
"path": "/404",
"labelKey": "not_found"
}
},
"Subscription": {
"enabled": true,
"props": {
"path": "/subscription",
"labelKey": "subscription"
},
"pages": {
"Invite": {
"enabled": true,
"props": {
"path": "/subscription/invite",
"labelKey": "invite"
}
},
"ManageCollaborators": {
"enabled": true,
"props": {
"path": "/subscription/manage-collaborators",
"labelKey": "manage_collaborators"
}
},
"Manage": {
"enabled": true,
"props": {
"path": "/subscription/manage",
"labelKey": "manage"
}
},
"New": {
"enabled": true,
"props": {
"path": "/subscription/new",
"labelKey": "subscribe"
}
},
"Error": {
"enabled": true,
"props": {
"path": "/subscription/error",
"labelKey": "error"
}
},
"Success": {
"enabled": true,
"props": {
"path": "/subscription/success",
"labelKey": "success"
}
},
"Subscribe": {
"enabled": true,
"props": {
"path": "/subscription/subscribe",
"labelKey": "subscribe"
},
"pages": {
"Standard": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/standard",
"labelKey": "standard"
}
},
"Illimity": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/illimity",
"labelKey": "illimity"
}
}
}
}
}
}
}
}

View File

@ -270,6 +270,80 @@
"path": "/404",
"labelKey": "not_found"
}
},
"Subscription": {
"enabled": true,
"props": {
"path": "/subscription",
"labelKey": "subscription"
},
"pages": {
"Invite": {
"enabled": true,
"props": {
"path": "/subscription/invite",
"labelKey": "invite"
}
},
"ManageCollaborators": {
"enabled": true,
"props": {
"path": "/subscription/manage-collaborators",
"labelKey": "manage_collaborators"
}
},
"Manage": {
"enabled": true,
"props": {
"path": "/subscription/manage",
"labelKey": "manage"
}
},
"New": {
"enabled": true,
"props": {
"path": "/subscription/new",
"labelKey": "subscribe"
}
},
"Error": {
"enabled": true,
"props": {
"path": "/subscription/error",
"labelKey": "error"
}
},
"Success": {
"enabled": true,
"props": {
"path": "/subscription/success",
"labelKey": "success"
}
},
"Subscribe": {
"enabled": true,
"props": {
"path": "/subscription/subscribe",
"labelKey": "subscribe"
},
"pages": {
"Standard": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/standard",
"labelKey": "standard"
}
},
"Illimity": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/illimity",
"labelKey": "illimity"
}
}
}
}
}
}
}
}

View File

@ -270,6 +270,80 @@
"path": "/404",
"labelKey": "not_found"
}
},
"Subscription": {
"enabled": true,
"props": {
"path": "/subscription",
"labelKey": "subscription"
},
"pages": {
"Invite": {
"enabled": true,
"props": {
"path": "/subscription/invite",
"labelKey": "invite"
}
},
"ManageCollaborators": {
"enabled": true,
"props": {
"path": "/subscription/manage-collaborators",
"labelKey": "manage_collaborators"
}
},
"Manage": {
"enabled": true,
"props": {
"path": "/subscription/manage",
"labelKey": "manage"
}
},
"New": {
"enabled": true,
"props": {
"path": "/subscription/new",
"labelKey": "subscribe"
}
},
"Error": {
"enabled": true,
"props": {
"path": "/subscription/error",
"labelKey": "error"
}
},
"Success": {
"enabled": true,
"props": {
"path": "/subscription/success",
"labelKey": "success"
}
},
"Subscribe": {
"enabled": true,
"props": {
"path": "/subscription/subscribe",
"labelKey": "subscribe"
},
"pages": {
"Standard": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/standard",
"labelKey": "standard"
}
},
"Illimity": {
"enabled": true,
"props": {
"path": "/subscription/subscribe/illimity",
"labelKey": "illimity"
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,5 @@
import SubscriptionInvite from "@Front/Components/Layouts/Subscription/SubscriptionInvite";
export default function Route() {
return <SubscriptionInvite />;
}

View File

@ -0,0 +1,5 @@
import SubscriptionManageCollaborators from "@Front/Components/Layouts/Subscription/SubscriptionManageCollaborators";
export default function Route() {
return <SubscriptionManageCollaborators />;
}