@stacksjs/defaults 0.70.54
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.md +21 -0
- package/README.md +2 -0
- package/package.json +20 -0
- package/resources/assets/fonts/013717e9.woff +0 -0
- package/resources/assets/fonts/03355817.woff2 +0 -0
- package/resources/assets/fonts/0380de7e.woff2 +0 -0
- package/resources/assets/fonts/0ba5d32a.woff +0 -0
- package/resources/assets/fonts/0bd57ddf.woff2 +0 -0
- package/resources/assets/fonts/19de0a60.woff2 +0 -0
- package/resources/assets/fonts/20b87e84.woff +0 -0
- package/resources/assets/fonts/4bbc67e1.woff +0 -0
- package/resources/assets/fonts/576fb0d7.woff +0 -0
- package/resources/assets/fonts/5ea10ee5.woff2 +0 -0
- package/resources/assets/fonts/6413fc0a.woff +0 -0
- package/resources/assets/fonts/72c114cf.woff +0 -0
- package/resources/assets/fonts/73c57e73.woff2 +0 -0
- package/resources/assets/fonts/813dca6c.woff +0 -0
- package/resources/assets/fonts/8a9b0995.woff2 +0 -0
- package/resources/assets/fonts/90b4832f.woff +0 -0
- package/resources/assets/fonts/9394ab85.woff2 +0 -0
- package/resources/assets/fonts/9b3c7d7f.woff2 +0 -0
- package/resources/assets/fonts/Monaco.ttf +0 -0
- package/resources/assets/fonts/b1b36d74.woff +0 -0
- package/resources/assets/fonts/b5d74798.woff2 +0 -0
- package/resources/assets/fonts/b9f81a4d.woff +0 -0
- package/resources/assets/fonts/c9784008.woff2 +0 -0
- package/resources/assets/fonts/cf692227.woff +0 -0
- package/resources/assets/fonts/d1c6ab51.woff2 +0 -0
- package/resources/assets/fonts/e2eb62fc.woff +0 -0
- package/resources/assets/fonts/fe21f8d8.woff2 +0 -0
- package/resources/assets/fonts/nps/CampmateScript-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/NPS_2026-variable.woff2 +0 -0
- package/resources/assets/fonts/nps/RedwoodSerif-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/SequoiaSans-Regular.woff2 +0 -0
- package/resources/assets/fonts/nps/Switchback-Regular.woff2 +0 -0
- package/resources/assets/images/app-icon.png +0 -0
- package/resources/assets/images/bg-img.png +0 -0
- package/resources/assets/images/park-ridge.svg +15 -0
- package/resources/assets/images/topography.svg +1 -0
- package/resources/assets/scripts/main.ts +2153 -0
- package/resources/assets/styles/README.md +19 -0
- package/resources/assets/styles/docs/main.css +1 -0
- package/resources/assets/styles/docs/overrides.css +1 -0
- package/resources/assets/styles/docs/vars.css +8 -0
- package/resources/assets/styles/main.css +1419 -0
- package/resources/components/Button.stx +70 -0
- package/resources/components/Buttons/BaseButton.stx +39 -0
- package/resources/components/Buttons/Counter.stx +28 -0
- package/resources/components/Buttons/ToggleDark.stx +11 -0
- package/resources/components/Container.stx +10 -0
- package/resources/components/Dashboard/.gitkeep +0 -0
- package/resources/components/Dashboard/Auth/ForgotPassword.stx +127 -0
- package/resources/components/Dashboard/Auth/Login.stx +212 -0
- package/resources/components/Dashboard/Auth/Register.stx +306 -0
- package/resources/components/Dashboard/Avatar.stx +123 -0
- package/resources/components/Dashboard/Billing/ActivePlan.stx +57 -0
- package/resources/components/Dashboard/Billing/CardBrands.stx +32 -0
- package/resources/components/Dashboard/Billing/CardForm.stx +34 -0
- package/resources/components/Dashboard/Billing/OneTimePayment.stx +28 -0
- package/resources/components/Dashboard/Billing/PaymentForm.stx +33 -0
- package/resources/components/Dashboard/Billing/PaymentMethod.stx +66 -0
- package/resources/components/Dashboard/Billing/PaymentMethodList.stx +52 -0
- package/resources/components/Dashboard/Billing/Plans.stx +136 -0
- package/resources/components/Dashboard/Buttons/AppButton.stx +48 -0
- package/resources/components/Dashboard/Buttons/IconButton.stx +20 -0
- package/resources/components/Dashboard/Card.stx +78 -0
- package/resources/components/Dashboard/Chart.stx +75 -0
- package/resources/components/Dashboard/CodeEditor.stx +41 -0
- package/resources/components/Dashboard/Commerce/BulkActionBar.stx +70 -0
- package/resources/components/Dashboard/Commerce/CategoriesTable.stx +133 -0
- package/resources/components/Dashboard/Commerce/CouponsTable.stx +161 -0
- package/resources/components/Dashboard/Commerce/CustomersTable.stx +142 -0
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +80 -0
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryTable.stx +85 -0
- package/resources/components/Dashboard/Commerce/Delivery/DriversTable.stx +99 -0
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeysTable.stx +92 -0
- package/resources/components/Dashboard/Commerce/Delivery/LicenseTemplatesTable.stx +88 -0
- package/resources/components/Dashboard/Commerce/Delivery/Pagination.stx +122 -0
- package/resources/components/Dashboard/Commerce/Delivery/SearchFilter.stx +23 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodsTable.stx +80 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRatesTable.stx +59 -0
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZonesTable.stx +71 -0
- package/resources/components/Dashboard/Commerce/Delivery/TabNavigation.stx +36 -0
- package/resources/components/Dashboard/Commerce/Forms/CouponsForm.stx +191 -0
- package/resources/components/Dashboard/Commerce/OrdersTable.stx +131 -0
- package/resources/components/Dashboard/Commerce/ProductsTable.stx +170 -0
- package/resources/components/Dashboard/CreateRecordModal.stx +363 -0
- package/resources/components/Dashboard/DashboardLayout.stx +155 -0
- package/resources/components/Dashboard/Deployments/ActivityFeed.stx +54 -0
- package/resources/components/Dashboard/Deployments/DeployScript.stx +23 -0
- package/resources/components/Dashboard/Deployments/DeploymentHistory.stx +97 -0
- package/resources/components/Dashboard/Deployments/DeploymentList.stx +94 -0
- package/resources/components/Dashboard/Deployments/LiveTerminalOutput.stx +18 -0
- package/resources/components/Dashboard/Elements/Dropdown.stx +57 -0
- package/resources/components/Dashboard/Email/EmailActivityChart.stx +111 -0
- package/resources/components/Dashboard/Email/EmailCompose.stx +72 -0
- package/resources/components/Dashboard/Email/EmailDetail.stx +105 -0
- package/resources/components/Dashboard/Email/EmailList.stx +117 -0
- package/resources/components/Dashboard/Email/EmailSearch.stx +40 -0
- package/resources/components/Dashboard/Email/EmailSidebar.stx +133 -0
- package/resources/components/Dashboard/GlobalSearch.stx +124 -0
- package/resources/components/Dashboard/MobileSidebar.stx +47 -0
- package/resources/components/Dashboard/Modals/BaseModal.stx +50 -0
- package/resources/components/Dashboard/Modals/Popups/Alert.stx +112 -0
- package/resources/components/Dashboard/Modals/Popups/Toast.stx +70 -0
- package/resources/components/Dashboard/Modals/ToastWrapper.stx +35 -0
- package/resources/components/Dashboard/Monitoring/ErrorDetailModal.stx +228 -0
- package/resources/components/Dashboard/Monitoring/ErrorsTable.stx +184 -0
- package/resources/components/Dashboard/Navbar.stx +194 -0
- package/resources/components/Dashboard/NavbarModern.stx +188 -0
- package/resources/components/Dashboard/NotificationErrorModal.stx +105 -0
- package/resources/components/Dashboard/NotificationStatusBadge.stx +20 -0
- package/resources/components/Dashboard/Pagination.stx +90 -0
- package/resources/components/Dashboard/Queue/QueueTable.stx +127 -0
- package/resources/components/Dashboard/Settings/Forms/AIForm.stx +126 -0
- package/resources/components/Dashboard/Settings/Forms/AnalyticsForm.stx +52 -0
- package/resources/components/Dashboard/Settings/Forms/AppForm.stx +157 -0
- package/resources/components/Dashboard/Settings/Forms/CLIForm.stx +66 -0
- package/resources/components/Dashboard/Settings/Forms/CacheForm.stx +253 -0
- package/resources/components/Dashboard/Settings/Forms/CloudForm.stx +441 -0
- package/resources/components/Dashboard/Settings/Forms/DNSForm.stx +307 -0
- package/resources/components/Dashboard/Settings/Forms/DatabaseForm.stx +168 -0
- package/resources/components/Dashboard/Settings/Forms/EmailForm.stx +100 -0
- package/resources/components/Dashboard/Settings/Forms/FileSystemForm.stx +259 -0
- package/resources/components/Dashboard/Settings/Forms/HashingForm.stx +119 -0
- package/resources/components/Dashboard/Settings/Forms/LibraryForm.stx +389 -0
- package/resources/components/Dashboard/Settings/Forms/QueueForm.stx +219 -0
- package/resources/components/Dashboard/Settings/Forms/SearchEngineForm.stx +48 -0
- package/resources/components/Dashboard/Settings/Forms/SecurityForm.stx +143 -0
- package/resources/components/Dashboard/Settings/Forms/ServicesForm.stx +194 -0
- package/resources/components/Dashboard/Settings/Forms/StorageForm.stx +191 -0
- package/resources/components/Dashboard/Settings/Forms/TeamsForm.stx +33 -0
- package/resources/components/Dashboard/Settings/SettingsFormManager.stx +181 -0
- package/resources/components/Dashboard/SettingsHeader.stx +88 -0
- package/resources/components/Dashboard/SettingsSidebar.stx +234 -0
- package/resources/components/Dashboard/Sidebar.stx +23 -0
- package/resources/components/Dashboard/SidebarModern.stx +249 -0
- package/resources/components/Dashboard/Skeleton/LoadingCard.stx +15 -0
- package/resources/components/Dashboard/Skeleton/LoadingDetails.stx +26 -0
- package/resources/components/Dashboard/Table.stx +82 -0
- package/resources/components/Dashboard/TerminalContainer.stx +97 -0
- package/resources/components/Dashboard/Tooltip.stx +166 -0
- package/resources/components/Dashboard/Transaction/index.stx +88 -0
- package/resources/components/Dashboard/UI/ActivityTable.stx +54 -0
- package/resources/components/Dashboard/UI/Avatar.stx +102 -0
- package/resources/components/Dashboard/UI/Badge.stx +66 -0
- package/resources/components/Dashboard/UI/Button.stx +73 -0
- package/resources/components/Dashboard/UI/Card.stx +51 -0
- package/resources/components/Dashboard/UI/ChartCard.stx +36 -0
- package/resources/components/Dashboard/UI/Checkbox.stx +74 -0
- package/resources/components/Dashboard/UI/ConfirmDialog.stx +74 -0
- package/resources/components/Dashboard/UI/DataTable.stx +45 -0
- package/resources/components/Dashboard/UI/Dropdown.stx +72 -0
- package/resources/components/Dashboard/UI/DropdownItem.stx +80 -0
- package/resources/components/Dashboard/UI/EmptyState.stx +50 -0
- package/resources/components/Dashboard/UI/FilterBar.stx +48 -0
- package/resources/components/Dashboard/UI/Input.stx +77 -0
- package/resources/components/Dashboard/UI/Modal.stx +75 -0
- package/resources/components/Dashboard/UI/PageHeader.stx +69 -0
- package/resources/components/Dashboard/UI/PageLayout.stx +84 -0
- package/resources/components/Dashboard/UI/Pagination.stx +126 -0
- package/resources/components/Dashboard/UI/QuickLinks.stx +31 -0
- package/resources/components/Dashboard/UI/RouterLink.stx +31 -0
- package/resources/components/Dashboard/UI/Select.stx +84 -0
- package/resources/components/Dashboard/UI/ServiceHealth.stx +38 -0
- package/resources/components/Dashboard/UI/StatsCard.stx +79 -0
- package/resources/components/Dashboard/UI/Table.stx +143 -0
- package/resources/components/Dashboard/UI/Tabs.stx +45 -0
- package/resources/components/Dashboard/UI/Textarea.stx +93 -0
- package/resources/components/Dashboard/UI/Toggle.stx +84 -0
- package/resources/components/Dashboard/UI/WindowControls.stx +135 -0
- package/resources/components/Dashboard/Widget.stx +89 -0
- package/resources/components/Dashboard/WindowControls.stx +119 -0
- package/resources/components/Dashboard/index.ts +106 -0
- package/resources/components/Docs/Demo/ComboboxCode.md +114 -0
- package/resources/components/Docs/Demo/ComboboxDemo.stx +91 -0
- package/resources/components/Docs/Demo/DialogCode.md +42 -0
- package/resources/components/Docs/Demo/DialogDemo.stx +76 -0
- package/resources/components/Docs/Demo/DocsPlayground.stx +39 -0
- package/resources/components/Docs/Demo/DropdownCode.md +109 -0
- package/resources/components/Docs/Demo/DropdownDemo.stx +82 -0
- package/resources/components/Docs/Demo/Hero.stx +100 -0
- package/resources/components/Docs/Demo/ListboxCode.md +87 -0
- package/resources/components/Docs/Demo/ListboxDemo.stx +59 -0
- package/resources/components/Docs/Demo/NotificationCode.md +173 -0
- package/resources/components/Docs/Demo/NotificationDemo.stx +99 -0
- package/resources/components/Docs/Demo/PopoverCode.md +161 -0
- package/resources/components/Docs/Demo/PopoverDemo.stx +96 -0
- package/resources/components/Docs/Demo/RadioGroupCode.md +98 -0
- package/resources/components/Docs/Demo/RadioGroupDemo.stx +53 -0
- package/resources/components/Docs/Demo/StepperCode.md +216 -0
- package/resources/components/Docs/Demo/StepperDemo.stx +137 -0
- package/resources/components/Docs/Demo/SwitchCode.md +24 -0
- package/resources/components/Docs/Demo/SwitchDemo.stx +28 -0
- package/resources/components/Docs/Home.stx +7 -0
- package/resources/components/Docs/HomeContributors.stx +23 -0
- package/resources/components/Docs/HomeSponsors.stx +17 -0
- package/resources/components/Docs/HomeTeam.stx +18 -0
- package/resources/components/Docs/TeamMember.stx +79 -0
- package/resources/components/Email/EmailButton.stx +32 -0
- package/resources/components/Email/EmailDivider.stx +20 -0
- package/resources/components/Email/EmailImage.stx +24 -0
- package/resources/components/Email/EmailLayout.stx +55 -0
- package/resources/components/Email/EmailLink.stx +22 -0
- package/resources/components/Email/EmailSection.stx +20 -0
- package/resources/components/Email/EmailText.stx +30 -0
- package/resources/components/Forum/ForumBreadcrumb.stx +38 -0
- package/resources/components/Forum/ForumCategory.stx +16 -0
- package/resources/components/Forum/ForumCategoryItem.stx +46 -0
- package/resources/components/Forum/ForumLayout.stx +36 -0
- package/resources/components/Forum/ForumPagination.stx +39 -0
- package/resources/components/Forum/ForumPost.stx +71 -0
- package/resources/components/Forum/ForumReplyForm.stx +76 -0
- package/resources/components/Forum/ForumSidebar.stx +86 -0
- package/resources/components/Forum/ForumTopicItem.stx +77 -0
- package/resources/components/Forum/ForumTopicList.stx +23 -0
- package/resources/components/HelloWorld.stx +253 -0
- package/resources/components/Logo.stx +10 -0
- package/resources/components/Marketing/CallToAction.stx +30 -0
- package/resources/components/Marketing/ComingSoon.stx +155 -0
- package/resources/components/Marketing/Faqs.stx +99 -0
- package/resources/components/Marketing/Feature.stx +32 -0
- package/resources/components/Marketing/Fields.stx +28 -0
- package/resources/components/Marketing/Footer.stx +33 -0
- package/resources/components/Marketing/Header.stx +51 -0
- package/resources/components/Marketing/Hero.stx +46 -0
- package/resources/components/Marketing/Layout.stx +26 -0
- package/resources/components/Marketing/NavLink.stx +13 -0
- package/resources/components/Marketing/Plan.stx +43 -0
- package/resources/components/Marketing/Pricing.stx +120 -0
- package/resources/components/Marketing/PrimaryFeatures.stx +80 -0
- package/resources/components/Marketing/PrimaryFeatures2.stx +98 -0
- package/resources/components/Marketing/SecondaryFeatures.stx +80 -0
- package/resources/components/Marketing/Testimonials.stx +120 -0
- package/resources/components/Pagination.stx +149 -0
- package/resources/components/Playground.stx +9 -0
- package/resources/components/README.md +87 -0
- package/resources/components/Storefront/AnnouncementBar.stx +17 -0
- package/resources/components/Storefront/CartDrawer.stx +227 -0
- package/resources/components/Storefront/CheckoutSteps.stx +50 -0
- package/resources/components/Storefront/Footer.stx +81 -0
- package/resources/components/Storefront/Header.stx +66 -0
- package/resources/components/Storefront/OrderSummary.stx +79 -0
- package/resources/components/Storefront/ProductCard.stx +63 -0
- package/resources/components/Storefront/ProductGallery.stx +88 -0
- package/resources/components/Storefront/QuantitySelector.stx +97 -0
- package/resources/components/index.ts +13 -0
- package/resources/emails/email-verification.stx +46 -0
- package/resources/emails/layouts/base.html +113 -0
- package/resources/emails/password-changed.stx +55 -0
- package/resources/emails/password-reset.stx +43 -0
- package/resources/emails/subscription-confirmation.stx +99 -0
- package/resources/emails/welcome.stx +48 -0
- package/resources/functions/README.md +28 -0
- package/resources/functions/counter.ts +13 -0
- package/resources/functions/dark.ts +4 -0
- package/resources/functions/dashboard/analytics.ts +238 -0
- package/resources/functions/dashboard/config-io.ts +316 -0
- package/resources/functions/dashboard/data.ts +375 -0
- package/resources/functions/dashboard/library.ts +250 -0
- package/resources/functions/dashboard/sidebar.ts +537 -0
- package/resources/functions/dashboard/system.ts +150 -0
- package/resources/layouts/403.stx +188 -0
- package/resources/layouts/404.stx +34 -0
- package/resources/layouts/README.md +14 -0
- package/resources/layouts/dashboard/default.stx +20 -0
- package/resources/layouts/dashboard/guest.stx +10 -0
- package/resources/layouts/default.stx +3 -0
- package/resources/layouts/maintenance.stx +11 -0
- package/resources/layouts/storefront.stx +73 -0
- package/resources/layouts/system-tray/default.stx +3 -0
- package/resources/modules/.gitkeep +0 -0
- package/resources/plugins/.gitkeep +0 -0
- package/resources/plugins/preloader.ts +308 -0
- package/resources/types/README.md +3 -0
- package/resources/views/cart.stx +149 -0
- package/resources/views/checkout/contact.stx +101 -0
- package/resources/views/checkout/payment.stx +114 -0
- package/resources/views/checkout/shipping.stx +120 -0
- package/resources/views/coming-soon.stx +9 -0
- package/resources/views/dashboard/custom-page.stx +45 -0
- package/resources/views/dashboard/index.stx +24 -0
- package/resources/views/emails/welcome.stx +0 -0
- package/resources/views/errors/tester.stx +26 -0
- package/resources/views/index.stx +2865 -0
- package/resources/views/orders/[id].stx +130 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
/**
|
|
3
|
+
* Create Record Modal
|
|
4
|
+
* Native-styled dialog for creating new model records
|
|
5
|
+
*
|
|
6
|
+
* Props passed from parent via attributes:
|
|
7
|
+
* - modalId: Unique identifier for the modal
|
|
8
|
+
* - modelName: Display name of the model (e.g., "User")
|
|
9
|
+
* - tableName: Database table name (e.g., "users")
|
|
10
|
+
* - columns: Array of column definitions from PRAGMA table_info
|
|
11
|
+
*
|
|
12
|
+
* These are passed directly as template variables, no need to redeclare.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Use the passed variables directly (they're already in scope from props)
|
|
16
|
+
// Just ensure defaults for safety
|
|
17
|
+
const _modalId = typeof modalId !== 'undefined' ? modalId : 'create-record-modal'
|
|
18
|
+
const _modelName = typeof modelName !== 'undefined' ? modelName : 'Record'
|
|
19
|
+
const _tableName = typeof tableName !== 'undefined' ? tableName : ''
|
|
20
|
+
const _columns = typeof columns !== 'undefined' ? columns : []
|
|
21
|
+
|
|
22
|
+
// Filter out auto-generated columns that shouldn't be in the form
|
|
23
|
+
const formColumns = _columns.filter(col => {
|
|
24
|
+
const name = col.name.toLowerCase()
|
|
25
|
+
// Skip auto-generated fields
|
|
26
|
+
if (name === 'id') return false
|
|
27
|
+
if (name === 'created_at' || name === 'updated_at') return false
|
|
28
|
+
if (name === 'deleted_at') return false
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Determine input type based on column type and name
|
|
33
|
+
function getInputType(col) {
|
|
34
|
+
const name = col.name.toLowerCase()
|
|
35
|
+
const type = (col.type || '').toUpperCase()
|
|
36
|
+
|
|
37
|
+
// Name-based detection
|
|
38
|
+
if (name.includes('email')) return 'email'
|
|
39
|
+
if (name.includes('password')) return 'password'
|
|
40
|
+
if (name.includes('phone') || name.includes('tel')) return 'tel'
|
|
41
|
+
if (name.includes('url') || name.includes('website') || name.includes('link')) return 'url'
|
|
42
|
+
if (name.includes('date') && !name.includes('_at')) return 'date'
|
|
43
|
+
if (name.includes('time') && !name.includes('_at')) return 'time'
|
|
44
|
+
if (name.includes('color')) return 'color'
|
|
45
|
+
|
|
46
|
+
// Type-based detection
|
|
47
|
+
if (type.includes('INT') || type.includes('NUMERIC') || type.includes('REAL') || type.includes('FLOAT') || type.includes('DOUBLE')) {
|
|
48
|
+
return 'number'
|
|
49
|
+
}
|
|
50
|
+
if (type.includes('BOOL')) return 'checkbox'
|
|
51
|
+
if (type.includes('TEXT') && (name.includes('description') || name.includes('content') || name.includes('body') || name.includes('bio'))) {
|
|
52
|
+
return 'textarea'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return 'text'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if field should be a select/dropdown
|
|
59
|
+
function isSelectField(col) {
|
|
60
|
+
const name = col.name.toLowerCase()
|
|
61
|
+
return name === 'status' || name === 'type' || name === 'role' || name.includes('_type') || name.includes('_status')
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
<!-- Native-styled Modal Dialog -->
|
|
65
|
+
<div ref="pageRoot">
|
|
66
|
+
<dialog
|
|
67
|
+
id="{{ modalId }}"
|
|
68
|
+
class="p-0 max-w-lg w-full bg-white backdrop:bg-black/50 dark:bg-neutral-900 border-0 rounded-xl shadow-2xl backdrop:backdrop-blur-sm modal-dialog"
|
|
69
|
+
>
|
|
70
|
+
<form method="dialog" class="modal-form">
|
|
71
|
+
<!-- Header - macOS style with vibrancy effect -->
|
|
72
|
+
<div class="flex items-center justify-between px-5 py-4 bg-neutral-50/80 dark:bg-neutral-800/80 border-b border-neutral-200 rounded-t-xl dark:border-neutral-700">
|
|
73
|
+
<div>
|
|
74
|
+
<h2 class="font-semibold text-base text-neutral-900 dark:text-white">
|
|
75
|
+
Create {{ modelName }}
|
|
76
|
+
</h2>
|
|
77
|
+
<p class="mt-0.5 text-neutral-500 text-xs dark:text-neutral-400">
|
|
78
|
+
Add a new record to {{ tableName }}
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
class="p-1.5 text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 hover:bg-neutral-200 dark:hover:bg-neutral-700 rounded-lg transition-colors modal-close-btn"
|
|
84
|
+
aria-label="Close"
|
|
85
|
+
>
|
|
86
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
87
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- Body - Form Fields -->
|
|
93
|
+
<div class="overflow-y-auto px-5 py-4 space-y-4 max-h-[60vh]">
|
|
94
|
+
@foreach(formColumns as col)
|
|
95
|
+
<div class="form-field">
|
|
96
|
+
<label
|
|
97
|
+
for="{{ modalId }}-{{ col.name }}"
|
|
98
|
+
class="block mb-1.5 font-medium text-neutral-700 text-sm dark:text-neutral-300"
|
|
99
|
+
>
|
|
100
|
+
{{ col.label }}
|
|
101
|
+
@if(col.notnull && col.dflt_value === null)
|
|
102
|
+
<span class="ml-0.5 text-red-500">*</span>
|
|
103
|
+
@endif
|
|
104
|
+
</label>
|
|
105
|
+
|
|
106
|
+
@if(isSelectField(col))
|
|
107
|
+
<!-- Select dropdown for status/type fields -->
|
|
108
|
+
<select
|
|
109
|
+
id="{{ modalId }}-{{ col.name }}"
|
|
110
|
+
name="{{ col.name }}"
|
|
111
|
+
class="px-3 py-2 w-full text-neutral-900 text-sm dark:text-white bg-white dark:bg-neutral-800 border border-neutral-300 rounded-lg dark:border-neutral-600 focus:border-transparent focus:ring-2 focus:ring-blue-500 transition-shadow"
|
|
112
|
+
{{ col.notnull && col.dflt_value === null ? 'required' : '' }}
|
|
113
|
+
>
|
|
114
|
+
<option value="">Select {{ col.label }}</option>
|
|
115
|
+
@if(col.name === 'status')
|
|
116
|
+
<option value="active">Active</option>
|
|
117
|
+
<option value="inactive">Inactive</option>
|
|
118
|
+
<option value="pending">Pending</option>
|
|
119
|
+
<option value="draft">Draft</option>
|
|
120
|
+
@elseif(col.name === 'role')
|
|
121
|
+
<option value="user">User</option>
|
|
122
|
+
<option value="admin">Admin</option>
|
|
123
|
+
<option value="moderator">Moderator</option>
|
|
124
|
+
@else
|
|
125
|
+
<option value="default">Default</option>
|
|
126
|
+
@endif
|
|
127
|
+
</select>
|
|
128
|
+
|
|
129
|
+
@elseif(getInputType(col) === 'textarea')
|
|
130
|
+
<!-- Textarea for long text -->
|
|
131
|
+
<textarea
|
|
132
|
+
id="{{ modalId }}-{{ col.name }}"
|
|
133
|
+
name="{{ col.name }}"
|
|
134
|
+
rows="3"
|
|
135
|
+
class="px-3 py-2 w-full text-neutral-900 text-sm dark:text-white bg-white dark:bg-neutral-800 border border-neutral-300 rounded-lg dark:border-neutral-600 focus:border-transparent focus:ring-2 focus:ring-blue-500 transition-shadow resize-none"
|
|
136
|
+
placeholder="Enter {{ col.label.toLowerCase() }}"
|
|
137
|
+
{{ col.notnull && col.dflt_value === null ? 'required' : '' }}
|
|
138
|
+
></textarea>
|
|
139
|
+
|
|
140
|
+
@elseif(getInputType(col) === 'checkbox')
|
|
141
|
+
<!-- Checkbox toggle -->
|
|
142
|
+
<label class="flex gap-3 items-center cursor-pointer">
|
|
143
|
+
<input
|
|
144
|
+
type="checkbox"
|
|
145
|
+
id="{{ modalId }}-{{ col.name }}"
|
|
146
|
+
name="{{ col.name }}"
|
|
147
|
+
value="1"
|
|
148
|
+
class="h-4 w-4 text-blue-600 border-neutral-300 rounded dark:border-neutral-600 focus:ring-2 focus:ring-blue-500 focus:ring-offset-0"
|
|
149
|
+
/>
|
|
150
|
+
<span class="text-neutral-600 text-sm dark:text-neutral-400">Enable {{ col.label.toLowerCase() }}</span>
|
|
151
|
+
</label>
|
|
152
|
+
|
|
153
|
+
@else
|
|
154
|
+
<!-- Standard input -->
|
|
155
|
+
<input
|
|
156
|
+
type="{{ getInputType(col) }}"
|
|
157
|
+
id="{{ modalId }}-{{ col.name }}"
|
|
158
|
+
name="{{ col.name }}"
|
|
159
|
+
class="px-3 py-2 w-full text-neutral-900 text-sm dark:text-white bg-white dark:bg-neutral-800 border border-neutral-300 rounded-lg dark:border-neutral-600 focus:border-transparent focus:ring-2 focus:ring-blue-500 transition-shadow"
|
|
160
|
+
placeholder="Enter {{ col.label.toLowerCase() }}"
|
|
161
|
+
{{ col.notnull && col.dflt_value === null ? 'required' : '' }}
|
|
162
|
+
{{ getInputType(col) === 'number' ? 'step="any"' : '' }}
|
|
163
|
+
/>
|
|
164
|
+
@endif
|
|
165
|
+
|
|
166
|
+
@if(col.dflt_value !== null)
|
|
167
|
+
<p class="mt-1 text-neutral-400 text-xs dark:text-neutral-500">
|
|
168
|
+
Default: {{ col.dflt_value }}
|
|
169
|
+
</p>
|
|
170
|
+
@endif
|
|
171
|
+
</div>
|
|
172
|
+
@endforeach
|
|
173
|
+
|
|
174
|
+
@if(formColumns.length === 0)
|
|
175
|
+
<div class="py-8 text-center text-neutral-500 dark:text-neutral-400">
|
|
176
|
+
<p>No editable fields available for this model.</p>
|
|
177
|
+
</div>
|
|
178
|
+
@endif
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<!-- Footer - Action buttons -->
|
|
182
|
+
<div class="flex gap-3 items-center justify-end px-5 py-4 bg-neutral-50/50 dark:bg-neutral-800/50 border-neutral-200 border-t rounded-b-xl dark:border-neutral-700">
|
|
183
|
+
<button
|
|
184
|
+
type="button"
|
|
185
|
+
class="px-4 py-2 font-medium text-neutral-700 text-sm dark:text-neutral-300 bg-white dark:bg-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-600 border border-neutral-300 rounded-lg dark:border-neutral-600 transition-colors modal-close-btn"
|
|
186
|
+
>
|
|
187
|
+
Cancel
|
|
188
|
+
</button>
|
|
189
|
+
<button
|
|
190
|
+
type="submit"
|
|
191
|
+
class="px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-neutral-900 disabled:opacity-50 transition-colors disabled:cursor-not-allowed modal-submit-btn"
|
|
192
|
+
{{ formColumns.length === 0 ? 'disabled' : '' }}
|
|
193
|
+
>
|
|
194
|
+
Create {{ modelName }}
|
|
195
|
+
</button>
|
|
196
|
+
</div>
|
|
197
|
+
</form>
|
|
198
|
+
</dialog>
|
|
199
|
+
</div>
|
|
200
|
+
<script client>
|
|
201
|
+
var _root = useRef('pageRoot')
|
|
202
|
+
onMount(function() {
|
|
203
|
+
var el = _root.current;
|
|
204
|
+
if (!el) return;
|
|
205
|
+
// Initialize all create record modals
|
|
206
|
+
el.querySelectorAll('.modal-dialog').forEach(dialog => {
|
|
207
|
+
const form = dialog.querySelector('.modal-form');
|
|
208
|
+
const closeButtons = dialog.querySelectorAll('.modal-close-btn');
|
|
209
|
+
const submitBtn = dialog.querySelector('.modal-submit-btn');
|
|
210
|
+
|
|
211
|
+
// Close on close button click
|
|
212
|
+
closeButtons.forEach(btn => {
|
|
213
|
+
btn.addEventListener('click', () => {
|
|
214
|
+
dialog.close();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Close on backdrop click
|
|
219
|
+
dialog.addEventListener('click', (e) => {
|
|
220
|
+
if (e.target === dialog) {
|
|
221
|
+
dialog.close();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Close on Escape key
|
|
226
|
+
dialog.addEventListener('keydown', (e) => {
|
|
227
|
+
if (e.key === 'Escape') {
|
|
228
|
+
dialog.close();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Handle form submission
|
|
233
|
+
form.addEventListener('submit', async (e) => {
|
|
234
|
+
e.preventDefault();
|
|
235
|
+
|
|
236
|
+
const formData = new FormData(form);
|
|
237
|
+
const data = Object.fromEntries(formData.entries());
|
|
238
|
+
|
|
239
|
+
// Get table name from the modal's data attribute or hidden field
|
|
240
|
+
const tableName = dialog.dataset.table || '';
|
|
241
|
+
const modelName = dialog.dataset.model || 'Record';
|
|
242
|
+
|
|
243
|
+
// Show loading state
|
|
244
|
+
submitBtn.disabled = true;
|
|
245
|
+
submitBtn.innerHTML = `
|
|
246
|
+
<svg class="inline -ml-1 mr-2 h-4 w-4 text-white animate-spin" fill="none" viewBox="0 0 24 24">
|
|
247
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
248
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
249
|
+
</svg>
|
|
250
|
+
Creating...
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
// Post to API endpoint
|
|
255
|
+
const response = await fetch(`/api/data/${tableName}`, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
headers: {
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify(data),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (response.ok) {
|
|
264
|
+
// Show success via Craft native alert if available
|
|
265
|
+
if (window.webkit?.messageHandlers?.craft) {
|
|
266
|
+
window.webkit.messageHandlers.craft.postMessage({
|
|
267
|
+
type: 'dialog',
|
|
268
|
+
action: 'showAlert',
|
|
269
|
+
data: {
|
|
270
|
+
title: `${modelName} Created`,
|
|
271
|
+
message: 'The record was created successfully.',
|
|
272
|
+
style: 'info'
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
dialog.close();
|
|
278
|
+
form.reset();
|
|
279
|
+
|
|
280
|
+
// Reload the page to show the new record
|
|
281
|
+
location.reload();
|
|
282
|
+
} else {
|
|
283
|
+
const errorData = await response.json().catch(() => ({}));
|
|
284
|
+
throw new Error(errorData.message || 'Failed to create record');
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
// Show error via Craft native alert if available
|
|
288
|
+
if (window.webkit?.messageHandlers?.craft) {
|
|
289
|
+
window.webkit.messageHandlers.craft.postMessage({
|
|
290
|
+
type: 'dialog',
|
|
291
|
+
action: 'showAlert',
|
|
292
|
+
data: {
|
|
293
|
+
title: 'Error',
|
|
294
|
+
message: error.message || 'Failed to create the record.',
|
|
295
|
+
style: 'critical'
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
alert('Error: ' + (error.message || 'Failed to create the record.'));
|
|
300
|
+
}
|
|
301
|
+
} finally {
|
|
302
|
+
// Reset button state
|
|
303
|
+
submitBtn.disabled = false;
|
|
304
|
+
submitBtn.innerHTML = `Create ${modelName}`;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
</script>
|
|
310
|
+
|
|
311
|
+
<style>
|
|
312
|
+
/* Native macOS-style dialog animations */
|
|
313
|
+
.modal-dialog {
|
|
314
|
+
animation: modal-appear 0.2s ease-out;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.modal-dialog::backdrop {
|
|
318
|
+
animation: backdrop-fade 0.2s ease-out;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@keyframes modal-appear {
|
|
322
|
+
from {
|
|
323
|
+
opacity: 0;
|
|
324
|
+
transform: scale(0.95) translateY(-10px);
|
|
325
|
+
}
|
|
326
|
+
to {
|
|
327
|
+
opacity: 1;
|
|
328
|
+
transform: scale(1) translateY(0);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@keyframes backdrop-fade {
|
|
333
|
+
from {
|
|
334
|
+
opacity: 0;
|
|
335
|
+
}
|
|
336
|
+
to {
|
|
337
|
+
opacity: 1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* Focus styles for accessibility */
|
|
342
|
+
.modal-dialog:focus {
|
|
343
|
+
outline: none;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* Scrollbar styling for the form area */
|
|
347
|
+
.modal-dialog .max-h-\[60vh\]::-webkit-scrollbar {
|
|
348
|
+
width: 6px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.modal-dialog .max-h-\[60vh\]::-webkit-scrollbar-track {
|
|
352
|
+
background: transparent;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.modal-dialog .max-h-\[60vh\]::-webkit-scrollbar-thumb {
|
|
356
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
357
|
+
border-radius: 3px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.dark .modal-dialog .max-h-\[60vh\]::-webkit-scrollbar-thumb {
|
|
361
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
362
|
+
}
|
|
363
|
+
</style>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
interface DashboardLayoutProps {
|
|
3
|
+
isMobileMenuOpen?: boolean
|
|
4
|
+
isMobile?: boolean
|
|
5
|
+
currentPath?: string
|
|
6
|
+
isDark?: boolean
|
|
7
|
+
fullWidth?: boolean
|
|
8
|
+
pageTitle?: string
|
|
9
|
+
pageDescription?: string
|
|
10
|
+
noPadding?: boolean
|
|
11
|
+
showSidebar?: boolean
|
|
12
|
+
showNavbar?: boolean
|
|
13
|
+
showWindowControls?: boolean
|
|
14
|
+
isSidebarCollapsed?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { isMobileMenuOpen, isMobile, currentPath = '/', isDark = false, fullWidth, pageTitle, pageDescription, noPadding, showSidebar = true, showNavbar = true, showWindowControls = false, isSidebarCollapsed = false } = defineProps<DashboardLayoutProps>()
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Dashboard Layout Component
|
|
21
|
+
* A unified layout wrapper that combines the modern sidebar and navbar
|
|
22
|
+
* for consistent dashboard page structure.
|
|
23
|
+
*
|
|
24
|
+
* When running inside Craft with --native-sidebar, the native macOS
|
|
25
|
+
* sidebar is rendered by Craft - we skip the web sidebar in that case.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Sidebar width calculation (matches Sidebar width)
|
|
29
|
+
function getSidebarWidth() {
|
|
30
|
+
if (isMobile) return 0
|
|
31
|
+
return isSidebarCollapsed ? 64 : 240
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getMainContentStyle() {
|
|
35
|
+
if (!showSidebar || isMobile) return ''
|
|
36
|
+
return `padding-left: ${getSidebarWidth()}px`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Determine if we should show the web sidebar
|
|
40
|
+
function shouldShowWebSidebar() {
|
|
41
|
+
return showSidebar
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
<script>
|
|
45
|
+
(function() {
|
|
46
|
+
if (typeof window === 'undefined') return;
|
|
47
|
+
|
|
48
|
+
// Detect native sidebar via multiple signals:
|
|
49
|
+
// 1. Craft bridge flag (WKUserScript injection)
|
|
50
|
+
// 2. Craft window API
|
|
51
|
+
// 3. URL parameter (reliable fallback when WKUserScript timing is off)
|
|
52
|
+
var hasNativeSidebarFlag = Boolean(window.__craftNativeSidebar);
|
|
53
|
+
var hasCraftWindowApi = Boolean(window.craft && window.craft.window);
|
|
54
|
+
var hasUrlParam = window.location.search.indexOf('native-sidebar=1') !== -1;
|
|
55
|
+
var hasNativeSidebar = hasNativeSidebarFlag || hasCraftWindowApi || hasUrlParam;
|
|
56
|
+
|
|
57
|
+
if (hasNativeSidebar) {
|
|
58
|
+
window.__craftNativeSidebar = true;
|
|
59
|
+
document.documentElement.setAttribute('data-craft-native-sidebar', 'true');
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
</script>
|
|
63
|
+
<!-- macOS-inspired layout with subtle background -->
|
|
64
|
+
<div class="min-h-screen bg-white dark:bg-neutral-950">
|
|
65
|
+
<!-- Mobile sidebar overlay -->
|
|
66
|
+
@if(isMobileMenuOpen && isMobile)
|
|
67
|
+
<div
|
|
68
|
+
class="lg:hidden fixed inset-0 z-40 bg-black/50 backdrop-blur-sm duration-300 transition-opacity"
|
|
69
|
+
data-action="close-mobile-menu"
|
|
70
|
+
></div>
|
|
71
|
+
@endif
|
|
72
|
+
|
|
73
|
+
<!-- Mobile sidebar (web-based, only when no native sidebar) -->
|
|
74
|
+
@if(isMobileMenuOpen && isMobile && shouldShowWebSidebar())
|
|
75
|
+
<div class="lg:hidden fixed inset-y-0 left-0 z-50 w-64 duration-300 transition-transform dashboard-web-sidebar">
|
|
76
|
+
<Sidebar />
|
|
77
|
+
</div>
|
|
78
|
+
@endif
|
|
79
|
+
|
|
80
|
+
<!-- Desktop sidebar (web-based, only when no native sidebar) -->
|
|
81
|
+
@if(shouldShowWebSidebar() && !isMobile)
|
|
82
|
+
<div class="dashboard-web-sidebar">
|
|
83
|
+
<Sidebar />
|
|
84
|
+
</div>
|
|
85
|
+
@endif
|
|
86
|
+
|
|
87
|
+
<!-- Main content area -->
|
|
88
|
+
<div
|
|
89
|
+
class="flex flex-col min-h-screen duration-300 transition-all dashboard-main-content"
|
|
90
|
+
style="{{ getMainContentStyle() }}"
|
|
91
|
+
>
|
|
92
|
+
<!-- Navbar -->
|
|
93
|
+
@if(showNavbar)
|
|
94
|
+
<NavbarModern
|
|
95
|
+
showWindowControls="{{ showWindowControls }}"
|
|
96
|
+
currentPath="{{ currentPath || '/' }}"
|
|
97
|
+
isDark="{{ isDark || false }}"
|
|
98
|
+
/>
|
|
99
|
+
@endif
|
|
100
|
+
|
|
101
|
+
<!-- Page header slot -->
|
|
102
|
+
@if($slots.header)
|
|
103
|
+
<div class="bg-white dark:bg-neutral-900 border-b border-neutral-200 dark:border-neutral-800">
|
|
104
|
+
<div class="px-4 lg:px-6 py-4 {{ fullWidth ? '' : 'max-w-7xl mx-auto' }}">
|
|
105
|
+
<slot name="header">
|
|
106
|
+
<!-- Default page header -->
|
|
107
|
+
@if(pageTitle)
|
|
108
|
+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
109
|
+
<div>
|
|
110
|
+
<h1 class="font-semibold text-2xl text-neutral-900 dark:text-white">
|
|
111
|
+
{{ pageTitle }}
|
|
112
|
+
</h1>
|
|
113
|
+
@if(pageDescription)
|
|
114
|
+
<p class="mt-1 text-neutral-500 text-sm dark:text-neutral-400">
|
|
115
|
+
{{ pageDescription }}
|
|
116
|
+
</p>
|
|
117
|
+
@endif
|
|
118
|
+
</div>
|
|
119
|
+
<slot name="header-actions" />
|
|
120
|
+
</div>
|
|
121
|
+
@endif
|
|
122
|
+
</slot>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
@endif
|
|
126
|
+
|
|
127
|
+
<!-- Main content -->
|
|
128
|
+
<main class="{{ noPadding ? '' : 'px-4 lg:px-6 py-6' }} flex-1">
|
|
129
|
+
<div class="{{ fullWidth ? '' : 'max-w-7xl mx-auto' }}">
|
|
130
|
+
<slot />
|
|
131
|
+
</div>
|
|
132
|
+
</main>
|
|
133
|
+
|
|
134
|
+
<!-- Footer slot -->
|
|
135
|
+
@if($slots.footer)
|
|
136
|
+
<footer class="bg-white dark:bg-neutral-900 border-neutral-200 border-t dark:border-neutral-800">
|
|
137
|
+
<div class="px-4 lg:px-6 py-4 {{ fullWidth ? '' : 'max-w-7xl mx-auto' }}">
|
|
138
|
+
<slot name="footer" />
|
|
139
|
+
</div>
|
|
140
|
+
</footer>
|
|
141
|
+
@endif
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- Global overlays/modals slot -->
|
|
145
|
+
<slot name="overlays" />
|
|
146
|
+
</div>
|
|
147
|
+
<style>
|
|
148
|
+
html[data-craft-native-sidebar='true'] .dashboard-web-sidebar {
|
|
149
|
+
display: none !important;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
html[data-craft-native-sidebar='true'] .dashboard-main-content {
|
|
153
|
+
padding-left: 0 !important;
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
interface ActivityFeedProps {
|
|
3
|
+
commits?: any[]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { commits = [] } = defineProps<ActivityFeedProps>()
|
|
7
|
+
|
|
8
|
+
function getTimeDifference(dateString) {
|
|
9
|
+
const date = new Date(dateString)
|
|
10
|
+
const now = new Date()
|
|
11
|
+
const diffMs = now.getTime() - date.getTime()
|
|
12
|
+
const diffMins = Math.floor(diffMs / (1000 * 60))
|
|
13
|
+
const diffHours = Math.floor(diffMins / 60)
|
|
14
|
+
const diffDays = Math.floor(diffHours / 24)
|
|
15
|
+
|
|
16
|
+
if (diffDays > 0) return `${diffDays}d ago`
|
|
17
|
+
if (diffHours > 0) return `${diffHours}h ago`
|
|
18
|
+
if (diffMins > 0) return `${diffMins}m ago`
|
|
19
|
+
return 'Just now'
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
<aside class="lg:bottom-0 lg:fixed lg:overflow-y-auto lg:right-0 lg:top-16 lg:w-96 bg-black/2 lg:border-l lg:border-white/5">
|
|
23
|
+
<header class="flex items-center justify-between px-4 py-4 sm:px-6 sm:py-6 lg:px-8 border-b border-white/5">
|
|
24
|
+
<h2 class="font-semibold leading-7 text-base dark:text-white">
|
|
25
|
+
Activity feed
|
|
26
|
+
</h2>
|
|
27
|
+
</header>
|
|
28
|
+
|
|
29
|
+
@if(commits.length > 0)
|
|
30
|
+
<ul role="list" class="divide-gray-200 divide-y">
|
|
31
|
+
@foreach(commits as commit, index)
|
|
32
|
+
<li class="px-4 py-4 sm:px-6 lg:px-8">
|
|
33
|
+
<div class="flex gap-x-3 items-center">
|
|
34
|
+
<a target="_new" href="{{ commit.author.html_url }}">
|
|
35
|
+
<img src="{{ commit.author.avatar_url }}" alt="" class="flex-none h-6 w-6 bg-gray-800 rounded-full">
|
|
36
|
+
</a>
|
|
37
|
+
<a target="_new" href="{{ commit.author.html_url }}" class="flex-auto font-semibold leading-6 text-sm truncate dark:text-white">
|
|
38
|
+
{{ commit.author.login }}
|
|
39
|
+
</a>
|
|
40
|
+
<time datetime="{{ commit.commit.committer.date }}" class="flex-none text-gray-600 text-xs">
|
|
41
|
+
{{ getTimeDifference(commit.commit.committer.date) }}
|
|
42
|
+
</time>
|
|
43
|
+
</div>
|
|
44
|
+
<p class="mt-3 text-gray-500 text-sm">
|
|
45
|
+
<a href="{{ commit.commit.url }}" class="font-mono text-gray-400 truncate hover:text-gray-500">
|
|
46
|
+
{{ commit.sha.slice(0, 10) }}
|
|
47
|
+
</a>
|
|
48
|
+
on <span class="text-gray-400">main</span>
|
|
49
|
+
</p>
|
|
50
|
+
</li>
|
|
51
|
+
@endforeach
|
|
52
|
+
</ul>
|
|
53
|
+
@endif
|
|
54
|
+
</aside>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="pt-12 px-4 sm:px-6 lg:px-8">
|
|
2
|
+
<div class="sm:flex sm:items-center">
|
|
3
|
+
<div class="sm:flex-auto">
|
|
4
|
+
<h1 class="font-semibold leading-6 text-base text-gray-900">
|
|
5
|
+
Deploy Script
|
|
6
|
+
</h1>
|
|
7
|
+
|
|
8
|
+
<p class="mt-2 text-gray-700 text-sm">
|
|
9
|
+
If you need a custom deploy script, you may specify it here.
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="flow-root mt-8">
|
|
15
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
16
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
17
|
+
<div class="overflow-hidden">
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|