mirror of
https://github.com/devonthesofa/obsidian-note-status.git
synced 2026-07-22 12:30:24 +00:00
Merge pull request #97 from devonthesofa/feature/templates-caracterization
Templates caracterization
This commit is contained in:
commit
fd7004d3a8
11 changed files with 162 additions and 19 deletions
|
|
@ -20,26 +20,85 @@ export const MarketplaceBrowseModal: React.FC<MarketplaceBrowseModalProps> = ({
|
|||
onClose,
|
||||
}) => {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [languageFilter, setLanguageFilter] = useState("");
|
||||
const [typeFilter, setTypeFilter] = useState("");
|
||||
|
||||
const allTemplates = useMemo(() => {
|
||||
return [...PREDEFINED_TEMPLATES, ...COMMUNITY_TEMPLATES];
|
||||
}, []);
|
||||
|
||||
const availableLanguages = useMemo(() => {
|
||||
const langs = new Set<string>();
|
||||
allTemplates.forEach((t) => {
|
||||
if (t.language) langs.add(t.language);
|
||||
});
|
||||
return Array.from(langs).sort();
|
||||
}, [allTemplates]);
|
||||
|
||||
const availableTypes = useMemo(() => {
|
||||
const types = new Set<string>();
|
||||
allTemplates.forEach((t) => {
|
||||
if (t.type) types.add(t.type);
|
||||
});
|
||||
return Array.from(types).sort();
|
||||
}, [allTemplates]);
|
||||
|
||||
const filteredTemplates = useMemo(() => {
|
||||
const allTemplates = [...PREDEFINED_TEMPLATES, ...COMMUNITY_TEMPLATES];
|
||||
return allTemplates.filter(
|
||||
(t) =>
|
||||
return allTemplates.filter((t) => {
|
||||
const matchesSearch =
|
||||
t.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
t.description.toLowerCase().includes(searchQuery.toLowerCase()),
|
||||
);
|
||||
}, [searchQuery]);
|
||||
t.description.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
|
||||
const matchesLanguage =
|
||||
!languageFilter || t.language === languageFilter;
|
||||
const matchesType = !typeFilter || t.type === typeFilter;
|
||||
|
||||
return matchesSearch && matchesLanguage && matchesType;
|
||||
});
|
||||
}, [allTemplates, searchQuery, languageFilter, typeFilter]);
|
||||
|
||||
return (
|
||||
<div className="template-editor-modal marketplace-browse-modal">
|
||||
<div className="template-editor-modal__header">
|
||||
<div className="marketplace-header-content">
|
||||
<h2>Template Marketplace</h2>
|
||||
<SearchFilter
|
||||
value={searchQuery}
|
||||
onFilterChange={setSearchQuery}
|
||||
placeholder="Search templates..."
|
||||
/>
|
||||
<div style={{ display: "flex", gap: "8px" }}>
|
||||
<SearchFilter
|
||||
value={searchQuery}
|
||||
onFilterChange={setSearchQuery}
|
||||
placeholder="Search templates..."
|
||||
/>
|
||||
{availableLanguages.length > 0 && (
|
||||
<select
|
||||
value={languageFilter}
|
||||
onChange={(e) =>
|
||||
setLanguageFilter(e.target.value)
|
||||
}
|
||||
className="dropdown"
|
||||
>
|
||||
<option value="">All Languages</option>
|
||||
{availableLanguages.map((lang) => (
|
||||
<option key={lang} value={lang}>
|
||||
{lang}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
{availableTypes.length > 0 && (
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={(e) => setTypeFilter(e.target.value)}
|
||||
className="dropdown"
|
||||
>
|
||||
<option value="">All Types</option>
|
||||
{availableTypes.map((type) => (
|
||||
<option key={type} value={type}>
|
||||
{type}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -65,6 +124,16 @@ export const MarketplaceBrowseModal: React.FC<MarketplaceBrowseModalProps> = ({
|
|||
? "Built-in"
|
||||
: "Community"}
|
||||
</span>
|
||||
{template.type && (
|
||||
<span className="template-badge badge-neutral">
|
||||
{template.type}
|
||||
</span>
|
||||
)}
|
||||
{template.language && (
|
||||
<span className="template-badge badge-neutral">
|
||||
{template.language}
|
||||
</span>
|
||||
)}
|
||||
{template.authorGithub && (
|
||||
<div className="marketplace-card-author">
|
||||
by{" "}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ export const TemplateEditorModal: React.FC<TemplateEditorModalProps> = ({
|
|||
const [isIdUnlocked, setIsIdUnlocked] = useState(false);
|
||||
const [isAuthorUnlocked, setIsAuthorUnlocked] = useState(false);
|
||||
const [description, setDescription] = useState(template?.description || "");
|
||||
const [language, setLanguage] = useState(template?.language || "");
|
||||
const [type, setType] = useState(template?.type || "");
|
||||
const [authorGithub, setAuthorGithub] = useState(
|
||||
template?.authorGithub || "",
|
||||
);
|
||||
|
|
@ -74,12 +76,24 @@ export const TemplateEditorModal: React.FC<TemplateEditorModalProps> = ({
|
|||
id: templateId,
|
||||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
language: language.trim() || undefined,
|
||||
type: type.trim() || undefined,
|
||||
authorGithub: authorGithub.trim() || undefined,
|
||||
statuses: statusesWithTemplateId,
|
||||
};
|
||||
|
||||
onSave(savedTemplate);
|
||||
}, [id, name, description, authorGithub, statuses, isValid, onSave]);
|
||||
}, [
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
language,
|
||||
type,
|
||||
authorGithub,
|
||||
statuses,
|
||||
isValid,
|
||||
onSave,
|
||||
]);
|
||||
|
||||
const handleNameChange = useCallback(
|
||||
(val: string) => {
|
||||
|
|
@ -218,6 +232,30 @@ export const TemplateEditorModal: React.FC<TemplateEditorModalProps> = ({
|
|||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
name="Template Type (Optional)"
|
||||
description="Categorize your template (e.g. Academic, Project Management, Writing, General)"
|
||||
>
|
||||
<Input
|
||||
variant="text"
|
||||
value={type}
|
||||
onChange={setType}
|
||||
placeholder="e.g. Project Management"
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
name="Language (Optional)"
|
||||
description="Primary language of the statuses in this template"
|
||||
>
|
||||
<Input
|
||||
variant="text"
|
||||
value={language}
|
||||
onChange={setLanguage}
|
||||
placeholder="e.g. English"
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
name="Author GitHub Username (Optional)"
|
||||
description={
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
id: "academic",
|
||||
name: "Academic research",
|
||||
description: "Status workflow for academic research and writing",
|
||||
language: "English",
|
||||
type: "Academic",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
@ -62,6 +64,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
name: "Colorful workflow",
|
||||
description:
|
||||
"A colorful set of workflow statuses with descriptive icons",
|
||||
language: "English",
|
||||
type: "General",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
@ -125,6 +129,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
id: "creative-writing",
|
||||
name: "Creative Writing",
|
||||
description: "Workflow for novelists and creative writers",
|
||||
language: "English",
|
||||
type: "Writing",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
@ -165,6 +171,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
name: "Starter Template",
|
||||
description:
|
||||
"A simplified set of essential workflow statuses to get you started.",
|
||||
language: "English",
|
||||
type: "General",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
@ -198,6 +206,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
id: "minimal",
|
||||
name: "Minimal workflow",
|
||||
description: "A simplified set of essential workflow statuses",
|
||||
language: "English",
|
||||
type: "General",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
@ -231,6 +241,8 @@ export const PREDEFINED_TEMPLATES: StatusTemplate[] = [
|
|||
id: "project",
|
||||
name: "Project management",
|
||||
description: "Status workflow for project management and tracking",
|
||||
language: "English",
|
||||
type: "Project Management",
|
||||
authorGithub: "soler1212",
|
||||
statuses: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "academic",
|
||||
"name": "Academic research",
|
||||
"description": "Status workflow for academic research and writing",
|
||||
"language": "English",
|
||||
"type": "Academic",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "colorful",
|
||||
"name": "Colorful workflow",
|
||||
"description": "A colorful set of workflow statuses with descriptive icons",
|
||||
"language": "English",
|
||||
"type": "General",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "creative-writing",
|
||||
"name": "Creative Writing",
|
||||
"description": "Workflow for novelists and creative writers",
|
||||
"language": "English",
|
||||
"type": "Writing",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "default-starter",
|
||||
"name": "Starter Template",
|
||||
"description": "A simplified set of essential workflow statuses to get you started.",
|
||||
"language": "English",
|
||||
"type": "General",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "minimal",
|
||||
"name": "Minimal workflow",
|
||||
"description": "A simplified set of essential workflow statuses",
|
||||
"language": "English",
|
||||
"type": "General",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"id": "project",
|
||||
"name": "Project management",
|
||||
"description": "Status workflow for project management and tracking",
|
||||
"language": "English",
|
||||
"type": "Project Management",
|
||||
"authorGithub": "soler1212",
|
||||
"statuses": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ export interface StatusTemplate {
|
|||
name: string;
|
||||
description: string;
|
||||
authorGithub?: string;
|
||||
language?: string;
|
||||
type?: string;
|
||||
isPredefined?: boolean;
|
||||
statuses: NoteStatus[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import { StatusTemplate } from "@/types/pluginSettings";
|
||||
import settingsService from "@/core/settingsService";
|
||||
import { PREDEFINED_TEMPLATES } from "@/constants/predefinedTemplates";
|
||||
import {
|
||||
PREDEFINED_TEMPLATES,
|
||||
COMMUNITY_TEMPLATES,
|
||||
} from "@/constants/predefinedTemplates";
|
||||
|
||||
/**
|
||||
* Check if a template is a custom user template (not from marketplace)
|
||||
*/
|
||||
export const isCustomTemplate = (template: StatusTemplate): boolean => {
|
||||
// Either matches an original marketplace template ID
|
||||
// Either matches an original predefined template
|
||||
const matchesPredefined = PREDEFINED_TEMPLATES.some(
|
||||
(pt) =>
|
||||
pt.id === template.id ||
|
||||
|
|
@ -14,9 +17,13 @@ export const isCustomTemplate = (template: StatusTemplate): boolean => {
|
|||
);
|
||||
if (matchesPredefined) return false;
|
||||
|
||||
// Or has metadata from marketplace
|
||||
const hasMetadata = !!template.authorGithub;
|
||||
if (hasMetadata) return false;
|
||||
// Or matches an original community template
|
||||
const matchesCommunity = COMMUNITY_TEMPLATES.some(
|
||||
(ct) =>
|
||||
ct.id === template.id ||
|
||||
ct.name.toLowerCase() === template.name.toLowerCase(),
|
||||
);
|
||||
if (matchesCommunity) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
|
@ -25,7 +32,11 @@ export const isCustomTemplate = (template: StatusTemplate): boolean => {
|
|||
* Check if a marketplace template has been modified by the user
|
||||
*/
|
||||
export const isTemplateModified = (template: StatusTemplate): boolean => {
|
||||
const original = PREDEFINED_TEMPLATES.find(
|
||||
const allMarketplaceTemplates = [
|
||||
...PREDEFINED_TEMPLATES,
|
||||
...COMMUNITY_TEMPLATES,
|
||||
];
|
||||
const original = allMarketplaceTemplates.find(
|
||||
(pt) =>
|
||||
pt.id === template.id ||
|
||||
pt.name.toLowerCase() === template.name.toLowerCase(),
|
||||
|
|
@ -104,4 +115,3 @@ export const generateTemplateId = (
|
|||
|
||||
return id;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue