@@ -0,0 +1,154 @@
|
||||
import type { PortfolioProjectViewMode, PortfolioSectionType } from "@prisma/client";
|
||||
|
||||
export type PortfolioWizardStep = "basics" | "content" | "sections" | "assets";
|
||||
|
||||
export type PortfolioWizardStepState = {
|
||||
key: PortfolioWizardStep;
|
||||
complete: boolean;
|
||||
summary: string;
|
||||
};
|
||||
|
||||
type ProjectBasicsProgressInput = {
|
||||
categoryId: string;
|
||||
slug: string;
|
||||
clientName: string;
|
||||
projectYear: string;
|
||||
sortOrder: string;
|
||||
viewMode: PortfolioProjectViewMode;
|
||||
};
|
||||
|
||||
type ProjectContentProgressInput = {
|
||||
titleAr: string;
|
||||
titleEn: string;
|
||||
titleDe: string;
|
||||
serviceLabelAr: string;
|
||||
serviceLabelEn: string;
|
||||
serviceLabelDe: string;
|
||||
summaryAr: string;
|
||||
summaryEn: string;
|
||||
summaryDe: string;
|
||||
};
|
||||
|
||||
type ProjectSectionProgressInput = {
|
||||
type: PortfolioSectionType;
|
||||
titleAr: string;
|
||||
titleEn: string;
|
||||
titleDe: string;
|
||||
bodyAr: string;
|
||||
bodyEn: string;
|
||||
bodyDe: string;
|
||||
linkUrl: string;
|
||||
mediaAssetId: string;
|
||||
};
|
||||
|
||||
type ProjectAssetProgressInput = {
|
||||
mediaAssetId: string;
|
||||
altAr: string;
|
||||
altEn: string;
|
||||
altDe: string;
|
||||
};
|
||||
|
||||
type PortfolioWizardProgressInput = {
|
||||
basics: ProjectBasicsProgressInput;
|
||||
content: ProjectContentProgressInput;
|
||||
sections: ProjectSectionProgressInput[];
|
||||
assets: ProjectAssetProgressInput[];
|
||||
};
|
||||
|
||||
function hasText(value: string) {
|
||||
return value.trim().length > 0;
|
||||
}
|
||||
|
||||
function isValidSlug(value: string) {
|
||||
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value.trim());
|
||||
}
|
||||
|
||||
function isValidYear(value: string) {
|
||||
const year = Number(value);
|
||||
|
||||
return Number.isInteger(year) && year >= 2000 && year <= 2100;
|
||||
}
|
||||
|
||||
function isValidSortOrder(value: string) {
|
||||
const sortOrder = Number(value);
|
||||
|
||||
return Number.isInteger(sortOrder) && sortOrder >= 0 && sortOrder <= 9999;
|
||||
}
|
||||
|
||||
export function isPortfolioSectionReady(section: ProjectSectionProgressInput) {
|
||||
const hasTitles = hasText(section.titleAr) && hasText(section.titleEn) && hasText(section.titleDe);
|
||||
|
||||
if (!hasTitles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (section.type === "GALLERY") {
|
||||
return hasText(section.mediaAssetId);
|
||||
}
|
||||
|
||||
if (section.type === "LINK") {
|
||||
return hasText(section.linkUrl);
|
||||
}
|
||||
|
||||
return hasText(section.bodyAr) && hasText(section.bodyEn) && hasText(section.bodyDe);
|
||||
}
|
||||
|
||||
export function isPortfolioAssetReady(asset: ProjectAssetProgressInput) {
|
||||
return (
|
||||
hasText(asset.mediaAssetId) &&
|
||||
hasText(asset.altAr) &&
|
||||
hasText(asset.altEn) &&
|
||||
hasText(asset.altDe)
|
||||
);
|
||||
}
|
||||
|
||||
export function getPortfolioWizardProgress(
|
||||
input: PortfolioWizardProgressInput,
|
||||
): PortfolioWizardStepState[] {
|
||||
const completedSections = input.sections.filter(isPortfolioSectionReady).length;
|
||||
const completedAssets = input.assets.filter(isPortfolioAssetReady).length;
|
||||
|
||||
return [
|
||||
{
|
||||
key: "basics",
|
||||
complete:
|
||||
hasText(input.basics.categoryId) &&
|
||||
isValidSlug(input.basics.slug) &&
|
||||
hasText(input.basics.clientName) &&
|
||||
isValidYear(input.basics.projectYear) &&
|
||||
isValidSortOrder(input.basics.sortOrder) &&
|
||||
Boolean(input.basics.viewMode),
|
||||
summary: "Category, slug, client, year, and order are set.",
|
||||
},
|
||||
{
|
||||
key: "content",
|
||||
complete:
|
||||
hasText(input.content.titleAr) &&
|
||||
hasText(input.content.titleEn) &&
|
||||
hasText(input.content.titleDe) &&
|
||||
hasText(input.content.serviceLabelAr) &&
|
||||
hasText(input.content.serviceLabelEn) &&
|
||||
hasText(input.content.serviceLabelDe) &&
|
||||
hasText(input.content.summaryAr) &&
|
||||
hasText(input.content.summaryEn) &&
|
||||
hasText(input.content.summaryDe),
|
||||
summary: "Title, service label, and summary exist in all locales.",
|
||||
},
|
||||
{
|
||||
key: "sections",
|
||||
complete: input.sections.length > 0 && completedSections === input.sections.length,
|
||||
summary: `${completedSections}/${input.sections.length} sections ready.`,
|
||||
},
|
||||
{
|
||||
key: "assets",
|
||||
complete: input.assets.length > 0 && completedAssets === input.assets.length,
|
||||
summary: `${completedAssets}/${input.assets.length} assets ready.`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getFirstIncompleteWizardStep(
|
||||
steps: PortfolioWizardStepState[],
|
||||
): PortfolioWizardStep | null {
|
||||
return steps.find((step) => !step.complete)?.key ?? null;
|
||||
}
|
||||
Reference in New Issue
Block a user