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
157 changes: 117 additions & 40 deletions playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import {useEffect, useRef, useState} from "react";
interface Settings {
apiKey: string;
templateId: string | null;
embedType?: "templateDetails" | "templateEditor";
permissions: string[];
features: Required<badge.TemplateEmbedFeatures>;
editorFeatures?: badge.TemplateEditorFeatures;
sdkPath: string;
googleFont: string;
primaryColor: string;
Expand All @@ -41,9 +43,21 @@ export function Playground() {
key: "state",
defaultValue: DEFAULT_SETTINGS,
serialize: JSON.stringify,
deserialize: JSON.parse as (
value: string | undefined,
) => typeof DEFAULT_SETTINGS,
deserialize: (value: string | undefined): Settings => {
if (!value) {
return DEFAULT_SETTINGS;
}

const parsed = JSON.parse(value) as Partial<Settings>;

return {
...DEFAULT_SETTINGS,
...parsed,
embedType: parsed.embedType ?? "templateDetails",
editorFeatures:
parsed.editorFeatures ?? badge.TEMPLATE_EDITOR_FEATURES_DEFAULT,
};
},
getInitialValueInEffect: false,
});

Expand All @@ -54,7 +68,10 @@ export function Playground() {

const [sdkCall, setSdkCall] = useState<{
sdkOptions: badge.SdkOptions;
embedTemplatePageOptions: badge.EmbedTemplatePageOptions;
options:
| badge.EmbedTemplatePageOptions
| badge.EmbedTemplateEditorPageOptions;
functionName: string;
}>();

function launchEmbed() {
Expand Down Expand Up @@ -95,31 +112,57 @@ export function Playground() {
const primaryColor = settings.primaryColor || undefined;
const neutralColor = settings.neutralColor || undefined;

const embedTemplatePageOptions: badge.EmbedTemplatePageOptions = {
templateId,
features: settings.features,
fonts: googleFont
? [
{
cssSrc: `https://fonts.googleapis.com/css2?family=${googleFont.replace(/ /g, "+")}&display=swap`,
},
]
: undefined,
appearance: {
fontFamily: googleFont,
colors: {
primary: primaryColor,
neutral: neutralColor,
},
const fonts = googleFont
? [
{
cssSrc: `https://fonts.googleapis.com/css2?family=${googleFont.replace(/ /g, "+")}&display=swap`,
},
]
: undefined;

const appearance = {
fontFamily: googleFont,
colors: {
primary: primaryColor,
neutral: neutralColor,
},
};

badge.embedTemplatePage(sdk, element, embedTemplatePageOptions);

setSdkCall({
sdkOptions,
embedTemplatePageOptions,
});
const embedType = settings.embedType ?? "templateDetails";
switch (embedType) {
case "templateEditor": {
const options: badge.EmbedTemplateEditorPageOptions = {
templateId,
features:
settings.editorFeatures ??
badge.TEMPLATE_EDITOR_FEATURES_DEFAULT,
fonts,
appearance,
};
badge.embedTemplateEditorPage(sdk, element, options);
setSdkCall({
sdkOptions,
options,
functionName: "embedTemplateEditorPage",
});
return;
}
case "templateDetails": {
const options: badge.EmbedTemplatePageOptions = {
templateId,
features: settings.features,
fonts,
appearance,
};
badge.embedTemplatePage(sdk, element, options);
setSdkCall({
sdkOptions,
options,
functionName: "embedTemplatePage",
});
return;
}
}
})
.catch(alert);
}
Expand Down Expand Up @@ -160,6 +203,7 @@ export function Playground() {
handleApiKeyChange(settings.apiKey);
}, [handleApiKeyChange, settings.apiKey]);

const embedType = settings.embedType ?? "templateDetails";
return (
<AppShell
header={{height: 60}}
Expand Down Expand Up @@ -193,6 +237,18 @@ export function Playground() {
settingChanged("templateId", value);
}}
/>
<Select
label="Embed Type"
defaultValue={embedType}
data={[
{value: "templateDetails", label: "Template Details"},
{value: "templateEditor", label: "Template Editor"},
]}
value={embedType}
onChange={(value) => {
settingChanged("embedType", value as Settings["embedType"]);
}}
/>
<MultiSelect
label="Permissions"
data={ALL_PERMISSIONS}
Expand All @@ -201,19 +257,38 @@ export function Playground() {
settingChanged("permissions", value);
}}
/>
<MultiSelect
label="Features"
data={Object.keys(ALL_FEATURES_DISABLED)}
value={Object.entries(settings.features)
.filter(([_, value]) => value)
.map(([key]) => key)}
onChange={(value) => {
settingChanged("features", {
...ALL_FEATURES_DISABLED,
...Object.fromEntries(value.map((flag) => [flag, true])),
});
}}
/>
{embedType === "templateDetails" && (
<MultiSelect
label="Features"
data={Object.keys(ALL_FEATURES_DISABLED)}
value={Object.entries(settings.features)
.filter(([_, value]) => value)
.map(([key]) => key)}
onChange={(value) => {
settingChanged("features", {
...ALL_FEATURES_DISABLED,
...Object.fromEntries(value.map((flag) => [flag, true])),
});
}}
/>
)}
{embedType === "templateEditor" && (
<MultiSelect
label="Editor Features"
data={Object.keys(badge.TEMPLATE_EDITOR_FEATURES_DEFAULT)}
value={Object.entries(
settings.editorFeatures ??
badge.TEMPLATE_EDITOR_FEATURES_DEFAULT,
)
.filter(([_, value]) => value)
.map(([key]) => key)}
onChange={(value) => {
const editorFeatures: badge.TemplateEditorFeatures =
Object.fromEntries(value.map((flag) => [flag, true]));
settingChanged("editorFeatures", editorFeatures);
}}
/>
)}
<Switch
label="Show Appearance Settings"
checked={appearanceVisible}
Expand Down Expand Up @@ -277,7 +352,7 @@ export function Playground() {
<Code block>{`
const sdk = badge.makeSdk(${JSON.stringify(sdkCall.sdkOptions, null, 2)});

badge.embedTemplatePage(sdk, element, ${JSON.stringify(sdkCall.embedTemplatePageOptions, null, 2)});`}</Code>
badge.${sdkCall.functionName}(sdk, element, ${JSON.stringify(sdkCall.options, null, 2)});`}</Code>
</>
)}
</Stack>
Expand All @@ -295,6 +370,7 @@ badge.embedTemplatePage(sdk, element, ${JSON.stringify(sdkCall.embedTemplatePage
const DEFAULT_SETTINGS: Settings = {
apiKey: "",
templateId: null,
embedType: "templateDetails",
permissions: [
"workspace:read",
"user:write",
Expand All @@ -307,6 +383,7 @@ const DEFAULT_SETTINGS: Settings = {
campaigns: true,
campaignEditor: true,
},
editorFeatures: badge.TEMPLATE_EDITOR_FEATURES_DEFAULT,
sdkPath: "",
googleFont: "",
primaryColor: "",
Expand Down
93 changes: 93 additions & 0 deletions src/calls/embedTemplateEditorPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type {BadgeSdk} from "../sdk.ts";
import type {AppearanceConfig, FontSource} from "../helpers/appearance.ts";
import {createEmbedIframe} from "../helpers/iframe.ts";
import {validatePermissions} from "../helpers/permissions.ts";
import {parseTokenPayload} from "../helpers/token.ts";
import {type SdkPermission} from "../helpers/permissions.ts";
import {
type TemplateEditorFeature,
type TemplateEditorFeatures,
} from "../helpers/embedFeatures.ts";

export interface EmbedTemplateEditorPageOptions {
/**
* id of the template to show
*/
templateId: string;
features?: TemplateEditorFeatures | undefined;
fonts?: FontSource[] | undefined;
appearance?: AppearanceConfig | undefined;
}

/**
* requires template:write
*/
export function embedTemplateEditorPage(
sdk: BadgeSdk,
element: HTMLElement,
{templateId, features, fonts, appearance}: EmbedTemplateEditorPageOptions,
): void {
const {workspaceHandle, permissions} = parseTokenPayload(sdk.token);

validatePermissions(permissions, REQUIRED_PERMISSIONS);

const iframe = createEmbedIframe({
token: sdk.token,
path: sdk.path,
pathHash: `/${workspaceHandle}/templates/${templateId}/edit`,
navigation: "bottom",
config: {
features: {
templateEditor:
Object.keys(features ?? {}).length > 0
? {...TEMPLATE_EDITOR_FEATURES_NONE, ...features}
: TEMPLATE_EDITOR_FEATURES_DEFAULT,
},
fonts,
appearance,
},
});

element.replaceChildren(iframe);
}

const REQUIRED_PERMISSIONS: SdkPermission[][] = [
["workspace:read", "workspace:write"],
["user:read", "user:write"],
["template:write", "template:read"],
];

export const TEMPLATE_EDITOR_FEATURES_DEFAULT = {
logo: true,
icon: true,
heading: true,
colors: true,
coverImage: true,
headerField: true,
secondaryFields: true,
barcodeType: false,
barcodeData: false,
barcodeText: false,
description: false,
backFields: true,
locationMessages: true,
appLinks: true,
expiry: false,
} satisfies Required<TemplateEditorFeatures>;

export const TEMPLATE_EDITOR_FEATURES_NONE = Object.fromEntries(
Object.keys(TEMPLATE_EDITOR_FEATURES_DEFAULT).map((key) => [key, false]),
) as Record<TemplateEditorFeature, false>;

export const TEMPLATE_EDITOR_FEATURES_ALL = Object.fromEntries(
Object.keys(TEMPLATE_EDITOR_FEATURES_DEFAULT).map((key) => [key, true]),
) as Record<TemplateEditorFeature, true>;

export const TEMPLATE_EDITOR_FEATURES_ENABLE_BARCODE = {
barcodeType: true,
barcodeData: true,
barcodeText: true,
} satisfies Pick<
Record<TemplateEditorFeature, true>,
"barcodeType" | "barcodeData" | "barcodeText"
>;
Loading
Loading