Skip to content

Commit

Permalink
Refactor Edit.astro to include pageTypes in StudioCMS configuration s…
Browse files Browse the repository at this point in the history
…etup
  • Loading branch information
Adammatthiesen committed Dec 26, 2024
1 parent 6dff3ac commit 3dbf691
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/studiocms/src/hooks/config-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const configSetup = defineUtility('astro:config:setup')(
{
name: 'StudioCMS (Default)',
identifier: 'studiocms',
pageTypes: [{ label: 'Normal (StudioCMS)', identifier: 'studiocms' }],
},
];

Expand Down
13 changes: 13 additions & 0 deletions packages/studiocms_core/src/schemas/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ export const StudioCMSPluginSchema = z.object({
* Label that is shown in the select input
*/
label: z.string(),
/**
* Identifier that is saved in the database
* @example
* // Single page type per plugin
* 'studiocms'
* '@studiocms/blog'
* // Multiple page types per plugin (Use unique identifiers for each type to avoid conflicts)
* '@mystudiocms/plugin:pageType1'
* '@mystudiocms/plugin:pageType2'
* '@mystudiocms/plugin:pageType3'
* '@mystudiocms/plugin:pageType4'
*/
identifier: z.string(),
/**
* Description that is shown below the "Page Content" header if this type is selected
*/
Expand Down
15 changes: 15 additions & 0 deletions packages/studiocms_core/src/sdk-utils/StudioCMSSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
CombinedRank,
CombinedUserData,
DeletionResponse,
FolderListItem,
FolderNode,
MultiPageInsert,
PageContentReturnId,
Expand Down Expand Up @@ -143,6 +144,20 @@ export class StudioCMSSDK {
return this.generateFolderTree(currentFolders);
}

/**
* Gets the available folders from the database.
*
* @returns A promise that resolves to an array of folder list items.
*/
public async getAvailableFolders(): Promise<FolderListItem[]> {
const folders: FolderListItem[] = [];
const currentFolders = await this.db.select().from(tsPageFolderStructure);
for (const { id, name } of currentFolders) {
folders.push({ id, name });
}
return folders;
}

/**
* Finds a node in the tree that matches the given URL path.
* @param tree - The root of the folder tree.
Expand Down
75 changes: 75 additions & 0 deletions packages/studiocms_core/src/sdk-utils/StudioCMSVirtualCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { StudioCMSCacheError } from './errors';
import type {
BaseCacheObject,
CombinedPageData,
FolderListCacheObject,
FolderListItem,
FolderNode,
FolderTreeCacheObject,
PageDataCacheObject,
Expand Down Expand Up @@ -32,6 +34,7 @@ export class StudioCMSVirtualCache {
private readonly VersionMapID: string = '__StudioCMS_Latest_Version';
private readonly FolderTreeMapID: string = '__StudioCMS_Folder_Tree';
private readonly PageFolderTreeMapID: string = '__StudioCMS_Page_Folder_Tree';
private readonly FolderListMapID: string = '__StudioCMS_Folder_List';
private readonly StudioCMSPkgId: string = 'studiocms';
private readonly CMSSiteConfigId = CMSSiteConfigId;
private readonly versionCacheLifetime = versionCacheLifetime;
Expand All @@ -44,6 +47,7 @@ export class StudioCMSVirtualCache {
private version = new Map<string, VersionCacheObject>();
private folderTree = new Map<string, FolderTreeCacheObject>();
private pageFolderTree = new Map<string, FolderTreeCacheObject>();
private FolderList = new Map<string, FolderListCacheObject>();

constructor(cacheConfig: ProcessedCacheConfig, sdkCore: STUDIOCMS_SDK) {
this.cacheConfig = cacheConfig;
Expand Down Expand Up @@ -140,8 +144,76 @@ export class StudioCMSVirtualCache {
};
}

private folderListReturn(data: FolderListItem[]): FolderListCacheObject {
return {
data,
lastCacheUpdate: new Date(),
};
}

// Folder Tree Utils

public async getFolderList(): Promise<FolderListCacheObject> {
try {
if (!this.isEnabled()) {
const folderList = await this.sdk.getAvailableFolders();

if (!folderList) {
throw new StudioCMSCacheError('Folder list not found in database');
}

return this.folderListReturn(folderList);
}

const list = this.FolderList.get(this.FolderListMapID);

if (!list || this.isCacheExpired(list)) {
const folderList = await this.sdk.getAvailableFolders();

if (!folderList) {
throw new StudioCMSCacheError('Folder list not found in database');
}

this.FolderList.set(this.FolderListMapID, this.folderListReturn(folderList));

return this.folderListReturn(folderList);
}

return list;
} catch (error) {
throw new StudioCMSCacheError('Error fetching folder list');
}
}

public async updateFolderList(): Promise<FolderListCacheObject> {
try {
const folderList = await this.sdk.getAvailableFolders();

if (!this.isEnabled()) {
return this.folderListReturn(folderList);
}

this.FolderList.set(this.FolderListMapID, this.folderListReturn(folderList));

return this.folderListReturn(folderList);
} catch (error) {
throw new StudioCMSCacheError('Error updating folder list');
}
}

public clearFolderList(): void {
// Check if caching is disabled
if (!this.isEnabled()) {
return;
}

// Clear the folder list cache
this.FolderList.clear();

// Return void
return;
}

/**
* Retrieves the folder tree from the cache or the database.
*
Expand Down Expand Up @@ -824,6 +896,7 @@ export class StudioCMSVirtualCache {
latestVersion: async () => await this.getVersion(),
folderTree: async () => await this.getFolderTree(),
pageFolderTree: async () => await this.getPageFolderTree(),
folderList: async () => await this.getFolderList(),
},
CLEAR: {
page: {
Expand All @@ -833,6 +906,7 @@ export class StudioCMSVirtualCache {
pages: () => this.clearAllPages(),
latestVersion: () => this.clearVersion(),
folderTree: () => this.clearFolderTree(),
folderList: () => this.clearFolderList(),
},
UPDATE: {
page: {
Expand All @@ -849,6 +923,7 @@ export class StudioCMSVirtualCache {
siteConfig: async (data: SiteConfig) => await this.updateSiteConfig(data),
latestVersion: async () => await this.updateVersion(),
folderTree: async () => await this.updateFolderTree(),
folderList: async () => await this.updateFolderList(),
},
};
}
Expand Down
27 changes: 27 additions & 0 deletions packages/studiocms_core/src/sdk-utils/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export interface FolderNode {
children: FolderNode[];
}

export interface FolderListItem {
id: string;
name: string;
}

export type AstroDBVirtualModule = typeof import('astro:db');

// ../../schemas/config/sdk.ts
Expand Down Expand Up @@ -171,10 +176,32 @@ export interface VersionCacheObject extends BaseCacheObject {
version: string;
}

/**
* Represents a cache object for folder tree data.
* Extends the BaseCacheObject interface.
*
* @interface FolderTreeCacheObject
* @extends {BaseCacheObject}
*
* @property {FolderNode[]} data - The folder tree data to be cached.
*/
export interface FolderTreeCacheObject extends BaseCacheObject {
data: FolderNode[];
}

/**
* Represents a cache object for folder list data.
* Extends the BaseCacheObject interface.
*
* @interface FolderListCacheObject
* @extends {BaseCacheObject}
*
* @property {FolderListItem[]} data - The folder list data to be cached.
*/
export interface FolderListCacheObject extends BaseCacheObject {
data: FolderListItem[];
}

/**
* Represents a cache object that stores pages and site configuration data.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
---
import '../../../styles/tiny-mde.css';
import { StudioCMSRoutes } from 'studiocms:lib';
import pluginsList from 'studiocms:plugins';
import { studioCMS_SDK_Cache } from 'studiocms:sdk/cache';
import { Input, Select } from '@studiocms/ui/components';
import TinyMDE from '../../component-scripts/TinyMDE.astro';
const pageTypeOptions = [{ label: 'Normal (StudioCMS)', value: 'studiocms' }];
type PluginList = {
label: string;
value: string;
}[];
const { data: folderList } = await studioCMS_SDK_Cache.GET.folderList();
const pageTypeOptions = pluginsList.flatMap(({ pageTypes }) => {
const pageTypeOutput: PluginList = [];
if (!pageTypes) {
return pageTypeOutput;
}
for (const { label, identifier } of pageTypes) {
pageTypeOutput.push({ label, value: identifier });
}
return pageTypeOutput;
});
const parentFolders = folderList.map(({ id: value, name: label }) => ({ label, value }));
const parentFolderOptions = [{ label: 'None', value: 'null' }, ...parentFolders];
const trueFalse = [
{ label: 'Yes', value: 'true' },
{ label: 'No', value: 'false' },
];
const categoriesOptions = [{ label: 'None', value: 'null' }];
const tagsOptions = [{ label: 'None', value: 'null' }];
const parentFolderOptions = [{ label: 'None', value: 'null' }];
---
<div id="edit-page-container" data-link={StudioCMSRoutes.sdk.pages}>
<form id="edit-page-form">
Expand All @@ -36,6 +62,7 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
label="Show in Navigation"
name="show-in-nav"
isRequired
defaultValue='false'
fullWidth
options={trueFalse}
/>
Expand All @@ -47,13 +74,13 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
</div>

<div class="form-row">
<Select label="Categories" name="categories" fullWidth options={categoriesOptions} disabled />
<Select label="Tags" name="tags" fullWidth options={tagsOptions} disabled />
<Select label="Categories" name="categories" placeholder="Work in Progress" fullWidth options={categoriesOptions} disabled />
<Select label="Tags" name="tags" placeholder="Work in Progress" fullWidth options={tagsOptions} disabled />
</div>

<div class="form-row">
<Select label="Show Author" name="show-author" fullWidth options={trueFalse} />
<Select label="Show Contributors" name="show-contributors" fullWidth options={trueFalse} />
<Select label="Show Author" name="show-author" defaultValue="false" fullWidth options={trueFalse} />
<Select label="Show Contributors" name="show-contributors" defaultValue="false" fullWidth options={trueFalse} />
</div>

<h2>Page Content</h2>
Expand Down Expand Up @@ -88,7 +115,6 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];

// get the elements
const editPageContainer = document.getElementById('edit-page-container');

const editPageForm = document.getElementById('edit-page-form');

const pageTitleInput = editPageForm.querySelector('input[name="page-title"]');
Expand All @@ -99,7 +125,6 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
const showInNavSelect = editPageForm.querySelector('select[name="show-in-nav"]');
const showInNavSelectValue = editPageForm.querySelector(`#show-in-nav-value-span`);
const pageHeroImageInput = editPageForm.querySelector('input[name="page-hero-image"]');

const categoriesSelect = editPageForm.querySelector('select[name="categories"]');
const categoriesSelectValue = editPageForm.querySelector(`#categories-value-span`);
const tagsSelect = editPageForm.querySelector('select[name="tags"]');
Expand All @@ -111,6 +136,8 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
const showContributorsSelect = editPageForm.querySelector('select[name="show-contributors"]');
const showContributorsSelectValue = editPageForm.querySelector(`#show-contributors-value-span`);

const parentFolderSelectContainer = editPageForm.querySelector('#parent-folder-container');

const pageContentTextarea = document.getElementById('page-content')
const editorToolbar = document.getElementById('editor-toolbar');

Expand Down Expand Up @@ -157,6 +184,10 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
pageDescriptionInput.value = pageData.data.description;
pageHeroImageInput.value = pageData.data.heroImage;

if (pageData.data.slug === 'index' || pageData.data.slug === 'about') {
parentFolderSelectContainer.classList.add('disabled');
}

setSelectValue(pageTypeSelect, pageTypeSelectValue, pageData.data.package, getPageTypeOption(pageData.data.package));
setSelectValue(showInNavSelect, showInNavSelectValue, pageData.data.showOnNav, pageData.data.showOnNav === true ? 'Yes' : 'No');
setSelectValue(showAuthorSelect, showAuthorSelectValue, pageData.data.showAuthor, pageData.data.showAuthor === true ? 'Yes' : 'No');
Expand Down Expand Up @@ -268,6 +299,26 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
pageContentTextarea.textContent = e.content;
});

tinyMDE.addEventListener("drop", function (event) {
let formData = new FormData();

// You can add use event.dataTransfer.items or event.dataTransfer.files
// to build the form data object:
for (let i = 0; i < event.dataTransfer.items.length; i++) {
if (event.dataTransfer.items[i].kind === "file") {
let file = event.dataTransfer.items[i].getAsFile();
formData.append("image", file);
}
}

// Call your API endpoint that accepts "Content-Type": "multipart/form-data"
// requests and responds with the image names and URL-s.
//
// Now you can add Markdown images like so:
// editor.paste(`![${imageName}](${imageUrl})`);
alert("Image upload coming soon!");
});

// Set default page content in the textarea
pageContentTextarea.textContent = pageData.data.defaultContent.content;

Expand All @@ -279,7 +330,7 @@ const parentFolderOptions = [{ label: 'None', value: 'null' }];
}

listener();
</script>
</script>

<style>
#edit-page-container {
Expand Down

0 comments on commit 3dbf691

Please sign in to comment.