diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 0e5048f5..65f6c5dd 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -23,6 +23,13 @@ "common.filter.value": "Filter by {0}", "common.error.message": "Sorry, something went wrong.", "common.openOnWebsite": "Open on website", + "common.settings": "Settings", + + "settings.contentTypes": "Content types", + "settings.contentFolders": "Content folders", + "settings.diagnostic": "Diagnostic", + "settings.diagnostic.description": "You can run the diagnostics to check the whole Front Matter CMS configuration.", + "settings.diagnostic.link": "Run full diagnostics", "developer.title": "Developer mode", "developer.reload.title": "Reload the dashboard", @@ -246,6 +253,7 @@ "dashboard.steps.stepsToGetStarted.showDashboard.description": "Once all actions are completed, the dashboard can be loaded.", "dashboard.steps.stepsToGetStarted.template.name": "Use a configuration template", "dashboard.steps.stepsToGetStarted.template.description": "Select a template to prefill the frontmatter.json file with the recommended settings.", + "dashboard.steps.stepsToGetStarted.astroContentTypes.name": "Create Content-Types for your Astro Content Collections", "dashboard.taxonomyView.button.add.title": "Add {0} to taxonomy settings", "dashboard.taxonomyView.button.edit.title": "Edit {0}", @@ -286,6 +294,9 @@ "dashboard.media.detailsSlideOver.unmapped.description": "Do you want to remap the metadata of unmapped files?", + "dashboard.configuration.astro.astroContentTypes.empty": "No Astro Content Collections found.", + "dashboard.configuration.astro.astroContentTypes.description": "The following Astro Content Collections and can be used to generate a content-type.", + "panel.contentType.contentTypeValidator.title": "Content-type", "panel.contentType.contentTypeValidator.hint": "We noticed field differences between the content-type and the front matter data. \n Would you like to create, update, or set the content-type for this content?", "panel.contentType.contentTypeValidator.button.create": "Create content-type", diff --git a/scripts/sync-localization.js b/scripts/sync-localization.js index c915f8e0..04c043c9 100644 --- a/scripts/sync-localization.js +++ b/scripts/sync-localization.js @@ -30,7 +30,7 @@ const glob = require('glob'); for (const key of enKeys) { // If the key does not exist in the file, add it if (!content[key]) { - content[key] = `🚧: ${enContent[key]}`; + content[key] = `${enContent[key]}`; } } diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index 03b7df40..7becdc3a 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -6,7 +6,7 @@ import { SETTING_EXTENSIBILITY_SCRIPTS } from '../constants'; import { join } from 'path'; -import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace } from 'vscode'; +import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from 'vscode'; import { Logger, Settings as SettingsHelper } from '../helpers'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { Extension } from '../helpers/Extension'; @@ -23,7 +23,8 @@ import { SnippetListener, TaxonomyListener, LogListener, - LocalizationListener + LocalizationListener, + SsgListener } from '../listeners/dashboard'; import { MediaListener as PanelMediaListener } from '../listeners/panel'; import { GitListener, ModeListener } from '../listeners/general'; @@ -180,6 +181,7 @@ export class Dashboard { GitListener.process(msg); TaxonomyListener.process(msg); LogListener.process(msg); + SsgListener.process(msg); }); } diff --git a/src/commands/Folders.ts b/src/commands/Folders.ts index f125ead1..221a4910 100644 --- a/src/commands/Folders.ts +++ b/src/commands/Folders.ts @@ -109,9 +109,12 @@ export class Folders { * Register the new folder path * @param folderInfo */ - public static async register(folderInfo: { title: string; path: Uri } | Uri) { + public static async register( + folderInfo: { title: string; path: Uri; contentType: string[] } | Uri + ) { let folderName = folderInfo instanceof Uri ? undefined : folderInfo.title; const folder = folderInfo instanceof Uri ? folderInfo : folderInfo.path; + const contentType = folderInfo instanceof Uri ? undefined : folderInfo.contentType; if (folder && folder.fsPath) { const wslPath = folder.fsPath.replace(/\//g, '\\'); @@ -137,10 +140,16 @@ export class Folders { }); } - folders.push({ + const contentFolder = { title: folderName, path: folder.fsPath - } as ContentFolder); + } as ContentFolder; + + if (contentType) { + contentFolder.contentTypes = typeof contentType === 'string' ? [contentType] : contentType; + } + + folders.push(contentFolder); folders = uniqBy(folders, (f) => f.path); await Folders.update(folders); diff --git a/src/constants/SsgScripts.ts b/src/constants/SsgScripts.ts new file mode 100644 index 00000000..a19cb4be --- /dev/null +++ b/src/constants/SsgScripts.ts @@ -0,0 +1,4 @@ +export const SsgScripts = { + folder: '/ssg-scripts', + astroContentCollection: 'astro-collections.mjs' +}; diff --git a/src/constants/index.ts b/src/constants/index.ts index e4a8731d..ca2aea05 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -14,6 +14,7 @@ export * from './NotificationType'; export * from './PreviewCommands'; export * from './SentryIgnore'; export * from './Snippet'; +export * from './SsgScripts'; export * from './StaticFolderPlaceholder'; export * from './TelemetryEvent'; export * from './Templates'; diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index 80c0b9a5..8610fc01 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -14,6 +14,8 @@ export enum DashboardMessage { addFolder = 'addFolder', addAssetsFolder = 'addAssetsFolder', triggerTemplate = 'triggerTemplate', + ssgGetAstroContentTypes = 'ssgGetAstroContentTypes', + ssgSetAstroContentTypes = 'ssgSetAstroContentTypes', // Content dashboard getData = 'getData', diff --git a/src/dashboardWebView/components/App.tsx b/src/dashboardWebView/components/App.tsx index 8e82e94a..213c9e61 100644 --- a/src/dashboardWebView/components/App.tsx +++ b/src/dashboardWebView/components/App.tsx @@ -21,6 +21,7 @@ import { ErrorView } from './ErrorView'; import { DashboardMessage } from '../DashboardMessage'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import { SettingsView } from './SettingsView/SettingsView'; export interface IAppProps { showWelcome: boolean; @@ -133,6 +134,8 @@ Stack: ${componentStack}` } /> )} + } /> + } /> diff --git a/src/dashboardWebView/components/Configuration/Astro/AstroContentTypes.tsx b/src/dashboardWebView/components/Configuration/Astro/AstroContentTypes.tsx new file mode 100644 index 00000000..400a6cff --- /dev/null +++ b/src/dashboardWebView/components/Configuration/Astro/AstroContentTypes.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import * as l10n from '@vscode/l10n'; +import { messageHandler } from '@estruyf/vscode/dist/client'; +import { DashboardMessage } from '../../../DashboardMessage'; +import { AstroCollection } from '../../../../models'; +import { Settings } from '../../../models'; +import { SelectItem } from '../../Steps/SelectItem'; +import { LocalizationKey } from '../../../../localization'; + +export interface IAstroContentTypesProps { + settings: Settings + triggerLoading: (isLoading: boolean) => void; +} + +export const AstroContentTypes: React.FunctionComponent = ({ + settings, + triggerLoading +}: React.PropsWithChildren) => { + const [collections, setCollections] = React.useState([]); + + React.useEffect(() => { + triggerLoading(true); + messageHandler.request(DashboardMessage.ssgGetAstroContentTypes).then((result) => { + triggerLoading(false); + setCollections(result); + }); + }, []); + + const generateContentType = (collection: AstroCollection) => { + triggerLoading(true); + messageHandler.request(DashboardMessage.ssgSetAstroContentTypes, { + collection + }).then((result) => { + triggerLoading(false); + }); + } + + if (!collections || collections.length === 0) { + return ( +
+ {l10n.t(LocalizationKey.dashboardConfigurationAstroAstroContentTypesEmpty)} +
+ ); + } + + return ( +
+

{l10n.t(LocalizationKey.dashboardConfigurationAstroAstroContentTypesDescription)}

+ +
+ { + collections.map((collection) => { + const ct = settings.contentTypes.find((c) => c.name === collection.name); + + return ( + generateContentType(collection)} + disabled={ct !== undefined} /> + ) + }) + } +
+
+ ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Configuration/Common/ContentFolders.tsx b/src/dashboardWebView/components/Configuration/Common/ContentFolders.tsx new file mode 100644 index 00000000..25ac1063 --- /dev/null +++ b/src/dashboardWebView/components/Configuration/Common/ContentFolders.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import * as l10n from '@vscode/l10n'; +import { LocalizationKey } from '../../../../localization'; +import { Settings } from '../../../models'; +import { Folder } from './Folder'; +import { Messenger } from '@estruyf/vscode/dist/client'; +import { DashboardMessage } from '../../../DashboardMessage'; + +export interface IContentFoldersProps { + settings: Settings + triggerLoading: (isLoading: boolean) => void; +} + +export const ContentFolders: React.FunctionComponent = ({ + settings +}: React.PropsWithChildren) => { + + const addFolder = (folder: string) => { + Messenger.send(DashboardMessage.addFolder, folder); + }; + + return ( + <> +

+ {l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedContentFoldersDescription)} +

+ + {settings?.dashboardState?.welcome?.contentFolders?.length > 0 && ( +
+
{l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedContentFoldersLabel)}
+
+ {settings?.dashboardState?.welcome?.contentFolders?.map((folder: string) => ( + + ))} +
+
+ )} + +

+ {l10n.t(LocalizationKey.commonInformation)}: {l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedContentFoldersInformationDescription)}. +

+ + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Configuration/Common/Folder.tsx b/src/dashboardWebView/components/Configuration/Common/Folder.tsx new file mode 100644 index 00000000..f3a3d219 --- /dev/null +++ b/src/dashboardWebView/components/Configuration/Common/Folder.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { join } from 'path'; +import * as l10n from '@vscode/l10n'; +import { LocalizationKey } from '../../../../localization'; +import { ContentFolder } from '../../../../models'; +import { SelectItem } from '../../Steps/SelectItem'; + +export interface IFolderProps { + wsFolder: string; + folder: string; + folders: ContentFolder[]; + addFolder: (folder: string) => void; +} + +export const Folder: React.FunctionComponent = ({ + wsFolder, + folder, + folders, + addFolder +}: React.PropsWithChildren) => { + + const isAdded = React.useMemo( + () => folders.find((f) => f.path.toLowerCase() === join(wsFolder, folder).toLowerCase()), + [folder, folders, wsFolder] + ); + + return ( + addFolder(folder)} /> + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index d8790a5e..d51c4537 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -31,6 +31,7 @@ import { Navigation } from './Navigation'; import { ProjectSwitcher } from './ProjectSwitcher'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { SettingsLink } from '../SettingsView/SettingsLink'; export interface IHeaderProps { header?: React.ReactNode; @@ -156,7 +157,11 @@ export const Header: React.FunctionComponent = ({ }`}> - +
+ + + +
{location.pathname === routePaths.contents && ( diff --git a/src/dashboardWebView/components/Header/Tab.tsx b/src/dashboardWebView/components/Header/Tab.tsx index 5d175e06..214115e5 100644 --- a/src/dashboardWebView/components/Header/Tab.tsx +++ b/src/dashboardWebView/components/Header/Tab.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { useLocation } from 'react-router-dom'; -import useThemeColors from '../../hooks/useThemeColors'; import { NavigationType } from '../../models'; export interface ITabProps { @@ -14,7 +13,6 @@ export const Tab: React.FunctionComponent = ({ children }: React.PropsWithChildren) => { const location = useLocation(); - const { getColors } = useThemeColors(); return ( + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/SettingsView/SettingsView.tsx b/src/dashboardWebView/components/SettingsView/SettingsView.tsx new file mode 100644 index 00000000..59123191 --- /dev/null +++ b/src/dashboardWebView/components/SettingsView/SettingsView.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import { PageLayout } from '../Layout'; +import { SponsorMsg } from '../Layout/SponsorMsg'; +import { useRecoilValue } from 'recoil'; +import { SettingsSelector } from '../../state'; +import { Spinner } from '../Common/Spinner'; +import { AstroContentTypes } from '../Configuration/Astro/AstroContentTypes'; +import { ContentFolders } from '../Configuration/Common/ContentFolders'; +import * as l10n from '@vscode/l10n'; +import { LocalizationKey } from '../../../localization'; + +export interface ISettingsViewProps { } + +export const SettingsView: React.FunctionComponent = (_: React.PropsWithChildren) => { + const [loading, setLoading] = React.useState(false); + const settings = useRecoilValue(SettingsSelector); + + return ( + + { + loading && + } + + { + settings && ( +
+

{l10n.t(LocalizationKey.commonSettings)}

+ +
+ { + settings?.crntFramework === 'astro' && ( +
+

{l10n.t(LocalizationKey.settingsContentTypes)}

+ + setLoading(isLoading)} /> +
+ ) + } + +
+

{l10n.t(LocalizationKey.settingsContentFolders)}

+ + setLoading(isLoading)} /> +
+ +
+

{l10n.t(LocalizationKey.settingsDiagnostic)}

+ +

{l10n.t(LocalizationKey.settingsDiagnosticDescription)}

+ +

+ + {l10n.t(LocalizationKey.settingsDiagnosticLink)} + +

+
+
+
+ ) + } + + + + Settings metrics +
+ ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Steps/SelectItem.tsx b/src/dashboardWebView/components/Steps/SelectItem.tsx index 21de3191..0adbc032 100644 --- a/src/dashboardWebView/components/Steps/SelectItem.tsx +++ b/src/dashboardWebView/components/Steps/SelectItem.tsx @@ -7,6 +7,7 @@ export interface ISelectItemProps { icon?: "add" | "select"; buttonTitle: string; isSelected: boolean; + disabled?: boolean; onClick: () => void; } @@ -15,6 +16,7 @@ export const SelectItem: React.FunctionComponent = ({ icon = "select", buttonTitle, isSelected, + disabled, onClick }: React.PropsWithChildren) => { return ( @@ -23,8 +25,9 @@ export const SelectItem: React.FunctionComponent = ({ >