astro-tractstack 2.0.0-rc.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -97
- package/README.md +7 -5
- package/bin/create-tractstack.js +31 -8
- package/dist/index.js +106 -29
- package/package.json +10 -5
- package/templates/css/frontend.css +1 -1
- package/templates/custom/minimal/CodeHook.astro +13 -12
- package/templates/custom/minimal/CustomRoutes.astro +25 -31
- package/templates/custom/with-examples/CodeHook.astro +22 -11
- package/templates/custom/with-examples/CustomRoutes.astro +4 -8
- package/templates/custom/with-examples/ProductCard.astro +29 -0
- package/templates/custom/with-examples/ProductCardWrapper.astro +43 -0
- package/templates/custom/with-examples/ProductGrid.astro +64 -0
- package/templates/custom/with-examples/pages/Collections.astro +58 -98
- package/templates/gitignore +42 -0
- package/templates/prettierignore +5 -0
- package/templates/prettierrc +19 -0
- package/templates/src/client/app.js +127 -0
- package/templates/src/client/htmx.min.js +3519 -0
- package/templates/src/client/view.js +429 -0
- package/templates/src/components/Footer.astro +4 -9
- package/templates/src/components/Header.astro +67 -60
- package/templates/src/components/Menu.tsx +188 -52
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +9 -13
- package/templates/src/components/codehooks/EpinetTableView.tsx +11 -7
- package/templates/src/components/codehooks/EpinetWrapper.tsx +1 -0
- package/templates/src/components/codehooks/FeaturedArticle.astro +105 -0
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +318 -0
- package/templates/src/components/codehooks/ListContent.astro +32 -162
- package/templates/src/components/codehooks/ListContentSetup.tsx +43 -138
- package/templates/src/components/codehooks/ProductCardSetup.tsx +152 -0
- package/templates/src/components/codehooks/ProductGridSetup.tsx +274 -0
- package/templates/src/components/codehooks/SearchWidget.tsx +453 -0
- package/templates/src/components/compositor/Node.tsx +3 -6
- package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +21 -11
- package/templates/src/components/compositor/elements/BunnyVideo.tsx +21 -20
- package/templates/src/components/compositor/nodes/Pane.tsx +51 -21
- package/templates/src/components/compositor/nodes/RenderChildren.tsx +6 -1
- package/templates/src/components/compositor/nodes/Widget.tsx +16 -2
- package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +155 -0
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +20 -1
- package/templates/src/components/edit/Header.tsx +10 -4
- package/templates/src/components/edit/PanelSwitch.tsx +11 -7
- package/templates/src/components/edit/SettingsPanel.tsx +29 -18
- package/templates/src/components/edit/ToolBar.tsx +1 -28
- package/templates/src/components/edit/ToolMode.tsx +45 -32
- package/templates/src/components/edit/pane/AddPanePanel_break.tsx +12 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +8 -2
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +1 -1
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +17 -27
- package/templates/src/components/edit/pane/PageGenSelector.tsx +16 -16
- package/templates/src/components/edit/pane/PageGenSpecial.tsx +26 -49
- package/templates/src/components/edit/pane/PageGen_preview.tsx +17 -2
- package/templates/src/components/edit/pane/PanePanel_path.tsx +2 -4
- package/templates/src/components/edit/pane/PanePanel_title.tsx +243 -76
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +17 -19
- package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +48 -37
- package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +60 -55
- package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +56 -50
- package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +54 -47
- package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +54 -44
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +54 -40
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +3 -3
- package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +56 -49
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +14 -5
- package/templates/src/components/edit/state/SaveModal.tsx +316 -169
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +1 -1
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +56 -55
- package/templates/src/components/edit/widgets/BunnyWidget.tsx +538 -59
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +656 -0
- package/templates/src/components/edit/widgets/ToggleWidget.tsx +9 -16
- package/templates/src/components/fields/ArtpackImage.tsx +4 -1
- package/templates/src/components/fields/BackgroundImage.tsx +1 -1
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +127 -35
- package/templates/src/components/fields/ColorPickerCombo.tsx +66 -62
- package/templates/src/components/fields/ImageUpload.tsx +1 -1
- package/templates/src/components/fields/ViewportComboBox.tsx +59 -42
- package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
- package/templates/src/components/form/ActionBuilderField.tsx +306 -87
- package/templates/src/components/search/SearchModal.tsx +420 -0
- package/templates/src/components/search/SearchResults.tsx +367 -0
- package/templates/src/components/search/SearchWrapper.tsx +46 -0
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +34 -8
- package/templates/src/components/storykeep/Dashboard_Branding.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Content.tsx +6 -0
- package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +38 -34
- package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +1 -1
- package/templates/src/components/storykeep/controls/content/MenuForm.tsx +56 -8
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +18 -3
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
- package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
- package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
- package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
- package/templates/src/constants/shapes.ts +9 -0
- package/templates/src/constants.ts +2121 -16
- package/templates/src/hooks/useSearch.ts +228 -0
- package/templates/src/layouts/Layout.astro +213 -104
- package/templates/src/lib/storyData.ts +4 -1
- package/templates/src/pages/[...slug]/edit.astro +14 -14
- package/templates/src/pages/[...slug].astro +82 -21
- package/templates/src/pages/api/orphan-analysis.ts +0 -1
- package/templates/src/pages/api/tailwind.ts +23 -21
- package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
- package/templates/src/pages/context/[...contextSlug].astro +7 -2
- package/templates/src/pages/storykeep/advanced.astro +5 -4
- package/templates/src/pages/storykeep/branding.astro +5 -4
- package/templates/src/pages/storykeep/content.astro +5 -4
- package/templates/src/pages/storykeep/init.astro +40 -1
- package/templates/src/pages/storykeep/login.astro +1 -1
- package/templates/src/pages/storykeep.astro +5 -4
- package/templates/src/stores/nodes.ts +59 -88
- package/templates/src/stores/orphanAnalysis.ts +19 -21
- package/templates/src/stores/storykeep.ts +7 -0
- package/templates/src/types/compositorTypes.ts +6 -0
- package/templates/src/types/tractstack.ts +17 -0
- package/templates/src/utils/actions/lispLexer.ts +2 -2
- package/templates/src/utils/actions/preParse_Action.ts +3 -0
- package/templates/src/utils/api/beliefHelpers.ts +12 -36
- package/templates/src/utils/api/menuHelpers.ts +2 -2
- package/templates/src/utils/api.ts +26 -0
- package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
- package/templates/src/utils/compositor/allowInsert.ts +5 -3
- package/templates/src/utils/compositor/nodesHelper.ts +4 -0
- package/templates/src/utils/compositor/processMarkdown.ts +16 -2
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
- package/templates/src/utils/compositor/typeGuards.ts +1 -0
- package/templates/src/utils/customHelpers.ts +38 -0
- package/templates/src/utils/helpers.ts +2 -2
- package/templates/src/utils/layout.ts +65 -144
- package/utils/inject-files.ts +95 -18
- package/templates/src/client/analytics-events.js +0 -207
- package/templates/src/client/belief-events.js +0 -191
- package/templates/src/client/sse.js +0 -613
- package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
- package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
- package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
- package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
// Define the Story interface for type safety
|
|
4
|
-
interface Story {
|
|
5
|
-
id: string;
|
|
6
|
-
title: string;
|
|
7
|
-
description: string;
|
|
8
|
-
slug: string;
|
|
9
|
-
changed: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Fake data for the featured story
|
|
13
|
-
const fakeFeaturedStory: Story = {
|
|
14
|
-
id: 'featured-1',
|
|
15
|
-
title: 'Sample Featured Story',
|
|
16
|
-
description: 'This is a sample description for the featured story.',
|
|
17
|
-
slug: 'sample-featured',
|
|
18
|
-
changed: '2023-01-01T00:00:00Z',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Fake data for included stories
|
|
22
|
-
const fakeIncludedStories: Story[] = [
|
|
23
|
-
{
|
|
24
|
-
id: 'story-1',
|
|
25
|
-
title: 'Sample Story 1',
|
|
26
|
-
description: 'Description for sample story 1.',
|
|
27
|
-
slug: 'sample-story-1',
|
|
28
|
-
changed: '2023-01-02T00:00:00Z',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: 'story-2',
|
|
32
|
-
title: 'Sample Story 2',
|
|
33
|
-
description: 'Description for sample story 2.',
|
|
34
|
-
slug: 'sample-story-2',
|
|
35
|
-
changed: '2023-01-03T00:00:00Z',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: 'story-3',
|
|
39
|
-
title: 'Sample Story 3',
|
|
40
|
-
description: 'Description for sample story 3.',
|
|
41
|
-
slug: 'sample-story-3',
|
|
42
|
-
changed: '2023-01-04T00:00:00Z',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: 'story-4',
|
|
46
|
-
title: 'Sample Story 4',
|
|
47
|
-
description: 'Description for sample story 4.',
|
|
48
|
-
slug: 'sample-story-4',
|
|
49
|
-
changed: '2023-01-05T00:00:00Z',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: 'story-5',
|
|
53
|
-
title: 'Sample Story 5',
|
|
54
|
-
description: 'Description for sample story 5.',
|
|
55
|
-
slug: 'sample-story-5',
|
|
56
|
-
changed: '2023-01-06T00:00:00Z',
|
|
57
|
-
},
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
// Utility function to format dates
|
|
61
|
-
const formatDate = (dateString: string): string => {
|
|
62
|
-
const date = new Date(dateString);
|
|
63
|
-
return new Intl.DateTimeFormat('en-US', {
|
|
64
|
-
year: 'numeric',
|
|
65
|
-
month: 'long',
|
|
66
|
-
day: 'numeric',
|
|
67
|
-
}).format(date);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Component definition
|
|
71
|
-
const FeaturedContentPreview = (): ReactNode => {
|
|
72
|
-
return (
|
|
73
|
-
<div className="flex flex-col gap-4 py-12 md:flex-row">
|
|
74
|
-
{/* Featured Story Section */}
|
|
75
|
-
<div className="p-4 md:w-3/5">
|
|
76
|
-
<div className="space-y-6 p-2">
|
|
77
|
-
{/* Placeholder for featured image */}
|
|
78
|
-
<div
|
|
79
|
-
style={{
|
|
80
|
-
width: '100%',
|
|
81
|
-
height: '300px',
|
|
82
|
-
backgroundColor: '#e0e0e0',
|
|
83
|
-
}}
|
|
84
|
-
className="rounded-lg"
|
|
85
|
-
/>
|
|
86
|
-
<h2 className="text-2xl font-bold text-black">
|
|
87
|
-
{fakeFeaturedStory.title}
|
|
88
|
-
</h2>
|
|
89
|
-
<p className="text-myblack text-base">
|
|
90
|
-
{fakeFeaturedStory.description}
|
|
91
|
-
</p>
|
|
92
|
-
<p className="text-mydarkgrey text-sm">
|
|
93
|
-
{formatDate(fakeFeaturedStory.changed)}
|
|
94
|
-
</p>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
{/* Included Stories Section */}
|
|
98
|
-
<div className="border-t-2 border-slate-100 p-4 md:w-2/5 md:border-l-2 md:border-t-0">
|
|
99
|
-
<div className="space-y-4">
|
|
100
|
-
{fakeIncludedStories.map((story) => (
|
|
101
|
-
<div key={story.id} className="flex items-start space-x-4 p-1">
|
|
102
|
-
{/* Placeholder for story image */}
|
|
103
|
-
<div
|
|
104
|
-
style={{
|
|
105
|
-
width: '100px',
|
|
106
|
-
height: '100px',
|
|
107
|
-
backgroundColor: '#d0d0d0',
|
|
108
|
-
}}
|
|
109
|
-
className="rounded-md"
|
|
110
|
-
/>
|
|
111
|
-
<div className="flex-1">
|
|
112
|
-
<h3 className="text-lg font-bold text-black">{story.title}</h3>
|
|
113
|
-
<p className="text-myblack line-clamp-2 text-sm">
|
|
114
|
-
{story.description}
|
|
115
|
-
</p>
|
|
116
|
-
<p className="text-mydarkgrey mt-1 text-xs">
|
|
117
|
-
{formatDate(story.changed)}
|
|
118
|
-
</p>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
))}
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export default FeaturedContentPreview;
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, type Dispatch, type SetStateAction } from 'react';
|
|
2
|
-
import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
|
|
3
|
-
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
|
|
4
|
-
import { getCtx } from '@/stores/nodes';
|
|
5
|
-
import { cloneDeep } from '@/utils/helpers';
|
|
6
|
-
import { PaneConfigMode, type PaneNode } from '@/types/compositorTypes';
|
|
7
|
-
|
|
8
|
-
interface PaneSlugPanelProps {
|
|
9
|
-
nodeId: string;
|
|
10
|
-
setMode: Dispatch<SetStateAction<PaneConfigMode>>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const PaneSlugPanel = ({ nodeId, setMode }: PaneSlugPanelProps) => {
|
|
14
|
-
const [slug, setSlug] = useState('');
|
|
15
|
-
const [isValid, setIsValid] = useState(false);
|
|
16
|
-
const [warning, setWarning] = useState(false);
|
|
17
|
-
const [charCount, setCharCount] = useState(0);
|
|
18
|
-
const [validationError, setValidationError] = useState<string | null>(null);
|
|
19
|
-
const [canSave, setCanSave] = useState(false);
|
|
20
|
-
|
|
21
|
-
const ctx = getCtx();
|
|
22
|
-
const allNodes = ctx.allNodes.get();
|
|
23
|
-
const paneNode = allNodes.get(nodeId) as PaneNode;
|
|
24
|
-
if (!paneNode) return null;
|
|
25
|
-
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
setSlug(paneNode.slug);
|
|
28
|
-
setCharCount(paneNode.slug.length);
|
|
29
|
-
checkLiveValidity(paneNode.slug);
|
|
30
|
-
}, [paneNode.slug]);
|
|
31
|
-
|
|
32
|
-
// More permissive validation for typing
|
|
33
|
-
const checkLiveValidity = (value: string) => {
|
|
34
|
-
const length = value.length;
|
|
35
|
-
setCharCount(length);
|
|
36
|
-
|
|
37
|
-
// Basic format check for allowed characters
|
|
38
|
-
if (!/^[a-z0-9-]*$/.test(value)) {
|
|
39
|
-
setValidationError(
|
|
40
|
-
'Only lowercase letters, numbers, and hyphens allowed'
|
|
41
|
-
);
|
|
42
|
-
setIsValid(false);
|
|
43
|
-
setCanSave(false);
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Length checks
|
|
48
|
-
setIsValid(length >= 3 && length <= 40);
|
|
49
|
-
setWarning(length > 40 && length <= 50);
|
|
50
|
-
setValidationError(null);
|
|
51
|
-
|
|
52
|
-
// Check if we can save
|
|
53
|
-
if (length >= 3) {
|
|
54
|
-
const saveValidation = checkSaveValidity(value);
|
|
55
|
-
setCanSave(saveValidation.isValid);
|
|
56
|
-
if (!saveValidation.isValid) {
|
|
57
|
-
setValidationError(saveValidation.error || null);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
setCanSave(false);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return true;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Strict validation for saving
|
|
67
|
-
const checkSaveValidity = (
|
|
68
|
-
value: string
|
|
69
|
-
): { isValid: boolean; error?: string } => {
|
|
70
|
-
// Strict pattern that prevents leading/trailing hyphens and multiple consecutive hyphens
|
|
71
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) {
|
|
72
|
-
return {
|
|
73
|
-
isValid: false,
|
|
74
|
-
error:
|
|
75
|
-
'Slug must start and end with letters or numbers, and no consecutive hyphens',
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check duplicates and reserved slugs
|
|
80
|
-
return ctx.isSlugValid(value, nodeId);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
84
|
-
const newSlug = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
85
|
-
|
|
86
|
-
if (newSlug.length <= 50) {
|
|
87
|
-
setSlug(newSlug);
|
|
88
|
-
checkLiveValidity(newSlug);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const handleSlugBlur = () => {
|
|
93
|
-
if (canSave) {
|
|
94
|
-
const ctx = getCtx();
|
|
95
|
-
const updatedNode = { ...cloneDeep(paneNode), slug, isChanged: true };
|
|
96
|
-
ctx.modifyNodes([updatedNode]);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<div className="group mb-4 w-full rounded-b-md bg-white px-1.5 py-6 shadow-inner">
|
|
102
|
-
<div className="px-3.5">
|
|
103
|
-
<div className="mb-4 flex justify-between">
|
|
104
|
-
<h3 className="text-lg font-bold">Pane Slug</h3>
|
|
105
|
-
<button
|
|
106
|
-
onClick={() => setMode(PaneConfigMode.DEFAULT)}
|
|
107
|
-
className="text-myblue hover:text-black"
|
|
108
|
-
>
|
|
109
|
-
← Go Back
|
|
110
|
-
</button>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<div className="relative">
|
|
114
|
-
<input
|
|
115
|
-
type="text"
|
|
116
|
-
value={slug}
|
|
117
|
-
onChange={handleSlugChange}
|
|
118
|
-
onBlur={handleSlugBlur}
|
|
119
|
-
onKeyDown={(e) => {
|
|
120
|
-
if (e.key === 'Enter') {
|
|
121
|
-
e.currentTarget.blur();
|
|
122
|
-
}
|
|
123
|
-
}}
|
|
124
|
-
className={`w-full rounded-md border px-2 py-1 pr-16 ${
|
|
125
|
-
validationError || charCount < 3
|
|
126
|
-
? 'border-red-500 bg-red-50'
|
|
127
|
-
: isValid && canSave
|
|
128
|
-
? 'border-green-500 bg-green-50'
|
|
129
|
-
: warning
|
|
130
|
-
? 'border-yellow-500 bg-yellow-50'
|
|
131
|
-
: 'border-gray-300'
|
|
132
|
-
}`}
|
|
133
|
-
placeholder="Enter pane slug (3-40 characters recommended)"
|
|
134
|
-
/>
|
|
135
|
-
<div className="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-2">
|
|
136
|
-
{validationError || charCount < 3 ? (
|
|
137
|
-
<ExclamationTriangleIcon className="h-5 w-5 text-red-500" />
|
|
138
|
-
) : isValid && canSave ? (
|
|
139
|
-
<CheckIcon className="h-5 w-5 text-green-500" />
|
|
140
|
-
) : warning ? (
|
|
141
|
-
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />
|
|
142
|
-
) : null}
|
|
143
|
-
<span
|
|
144
|
-
className={`text-sm ${
|
|
145
|
-
validationError || charCount < 3
|
|
146
|
-
? 'text-red-500'
|
|
147
|
-
: isValid && canSave
|
|
148
|
-
? 'text-green-500'
|
|
149
|
-
: warning
|
|
150
|
-
? 'text-yellow-500'
|
|
151
|
-
: 'text-gray-500'
|
|
152
|
-
}`}
|
|
153
|
-
>
|
|
154
|
-
{charCount}/50
|
|
155
|
-
</span>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
{validationError && (
|
|
159
|
-
<div className="mt-2 text-sm text-red-600">
|
|
160
|
-
<ExclamationTriangleIcon className="mr-1 inline h-4 w-4" />
|
|
161
|
-
{validationError}
|
|
162
|
-
</div>
|
|
163
|
-
)}
|
|
164
|
-
<div className="mt-4 space-y-4 text-lg">
|
|
165
|
-
<div className="text-gray-600">
|
|
166
|
-
This is solely used for analytics!
|
|
167
|
-
<ul className="ml-4 mt-1">
|
|
168
|
-
<li>
|
|
169
|
-
<CheckIcon className="inline h-4 w-4" /> Keep it concise and
|
|
170
|
-
descriptive
|
|
171
|
-
</li>
|
|
172
|
-
<li>
|
|
173
|
-
<CheckIcon className="inline h-4 w-4" /> Use lowercase letters
|
|
174
|
-
and numbers
|
|
175
|
-
</li>
|
|
176
|
-
<li>
|
|
177
|
-
<CheckIcon className="inline h-4 w-4" /> Use hyphens between
|
|
178
|
-
words
|
|
179
|
-
</li>
|
|
180
|
-
<li>
|
|
181
|
-
<CheckIcon className="inline h-4 w-4" /> Must start and end with
|
|
182
|
-
a letter or number
|
|
183
|
-
</li>
|
|
184
|
-
</ul>
|
|
185
|
-
</div>
|
|
186
|
-
<div className="py-4">
|
|
187
|
-
{charCount < 3 && (
|
|
188
|
-
<span className="text-red-500">
|
|
189
|
-
Slug must be at least 3 characters
|
|
190
|
-
</span>
|
|
191
|
-
)}
|
|
192
|
-
{charCount >= 3 && charCount < 5 && !validationError && (
|
|
193
|
-
<span className="text-gray-500">
|
|
194
|
-
Consider adding more characters for better description
|
|
195
|
-
</span>
|
|
196
|
-
)}
|
|
197
|
-
{warning && !validationError && (
|
|
198
|
-
<span className="text-yellow-500">
|
|
199
|
-
Slug is getting long - consider shortening it
|
|
200
|
-
</span>
|
|
201
|
-
)}
|
|
202
|
-
{isValid && canSave && charCount >= 5 && !validationError && (
|
|
203
|
-
<span className="text-green-500">
|
|
204
|
-
Good slug length and format!
|
|
205
|
-
</span>
|
|
206
|
-
)}
|
|
207
|
-
{isValid && !canSave && !validationError && (
|
|
208
|
-
<span className="text-gray-500">
|
|
209
|
-
Valid characters but needs proper formatting to save
|
|
210
|
-
</span>
|
|
211
|
-
)}
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
export default PaneSlugPanel;
|