From cd903699793d5d1d9eaf16f1dd4c4ae951f84f95 Mon Sep 17 00:00:00 2001 From: Tahsin Shahriar Date: Sun, 16 Aug 2026 19:51:06 +0600 Subject: [PATCH 01/13] dx: fix dev scripts and add missing newline - npm run dev now runs composer run dev (starts everything) - npm run dev:server runs just vite (for when you only need the frontend) - Added missing newline at end of NavBar.vue --- composer.json | 2 +- package.json | 3 ++- resources/js/components/NavBar.vue | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5e02af0..44d38c5 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ ], "dev": [ "Composer\\Config::disableProcessTimeout", - "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve --host=localhost\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php bin/dev-logs.php\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" + "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve --host=localhost\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php bin/dev-logs.php\" \"npm run dev:server\" --names=server,queue,logs,vite --kill-others" ], "lint": [ "pint --parallel" diff --git a/package.json b/package.json index c14636a..9775f7a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "scripts": { "build": "vite build", "build:ssr": "vite build && vite build --ssr", - "dev": "vite", + "dev": "composer run dev", + "dev:server": "vite", "format": "prettier --write resources/", "format:check": "prettier --check resources/", "lint": "eslint . --fix", diff --git a/resources/js/components/NavBar.vue b/resources/js/components/NavBar.vue index 250f68e..faa8597 100644 --- a/resources/js/components/NavBar.vue +++ b/resources/js/components/NavBar.vue @@ -183,4 +183,4 @@ onBeforeUnmount(() => { - \ No newline at end of file + From 0862cc684971abdd592f70a3250f9117703b7504 Mon Sep 17 00:00:00 2001 From: Tahsin Shahriar Date: Sun, 16 Aug 2026 21:30:02 +0600 Subject: [PATCH 02/13] feat: adopt Konsta UI - Phase 0 setup + Phase 1 component migration Phase 0: - Install konsta, import theme CSS + Roboto font - Create KonstaProvider.vue (OS-based iOS/Material theme detection) - Wrap both layouts (AppLayout, AdminLayout) with KonstaProvider - Add TypeScript declarations for konsta/vue Phase 1 - Migrated components: - NavBar: kNavbar, kButton, kList, kListItem - ToastNotification: kToast - NoticeDialog: kDialog - PwaInstallPrompt: kDialog (modal variant) - SubjectCard, BlogCard, UserCard: kCard - NodeRow, ResourceRow, BlogRow: kList + kListItem - FAQSection: kBlock + kBlockTitle (kept custom accordion) --- package-lock.json | 13 + package.json | 1 + resources/css/app.css | 3 + resources/js/components/BlogCard.vue | 50 ++-- resources/js/components/BlogRow.vue | 101 ++++--- resources/js/components/BreadcrumbNav.vue | 2 +- resources/js/components/FAQSection.vue | 25 +- resources/js/components/KonstaProvider.vue | 35 +++ resources/js/components/NavBar.vue | 91 +++--- resources/js/components/NodeRow.vue | 77 ++--- resources/js/components/NoticeDialog.vue | 95 +++---- resources/js/components/PwaInstallPrompt.vue | 7 +- resources/js/components/ResourceRow.vue | 111 ++++---- resources/js/components/SubjectCard.vue | 87 +++--- resources/js/components/ToastNotification.vue | 98 ++----- resources/js/components/UserCard.vue | 210 +++++++------- resources/js/layouts/AdminLayout.vue | 103 +++---- resources/js/layouts/AppLayout.vue | 49 ++-- resources/js/types/global.d.ts | 269 ++++++++++++++++++ 19 files changed, 886 insertions(+), 541 deletions(-) create mode 100644 resources/js/components/KonstaProvider.vue diff --git a/package-lock.json b/package-lock.json index 8d53271..3ab4d79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@vitejs/plugin-vue": "^6.0.0", "clsx": "^2.1.1", "concurrently": "^9.0.1", + "konsta": "^5.3.0", "laravel-vite-plugin": "^3.1", "lucide-vue-next": "^1.0.0", "tailwind-merge": "^3.2.0", @@ -6937,6 +6938,18 @@ "json-buffer": "3.0.1" } }, + "node_modules/konsta": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/konsta/-/konsta-5.3.0.tgz", + "integrity": "sha512-4KjAcQiczKSvbr/hgx9g0CLoJStqjE3DvAIhFJeN05WGi8lYu5b/6f2hdwnmtiq6K4QumY96usStEofH21pdbQ==", + "license": "MIT", + "dependencies": { + "tailwind-merge": "^3.3.1" + }, + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/laravel-precognition": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/laravel-precognition/-/laravel-precognition-2.0.0.tgz", diff --git a/package.json b/package.json index 9775f7a..46196e9 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@vitejs/plugin-vue": "^6.0.0", "clsx": "^2.1.1", "concurrently": "^9.0.1", + "konsta": "^5.3.0", "laravel-vite-plugin": "^3.1", "lucide-vue-next": "^1.0.0", "tailwind-merge": "^3.2.0", diff --git a/resources/css/app.css b/resources/css/app.css index f12718b..dfd1d95 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,8 +1,11 @@ @import 'tailwindcss'; +@import 'konsta/vue/theme.css'; @source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; @source '../../storage/framework/views/*.php'; +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap'); + @font-face { font-family: 'Anek Bangla'; font-style: normal; diff --git a/resources/js/components/BlogCard.vue b/resources/js/components/BlogCard.vue index 086840a..d31fa30 100644 --- a/resources/js/components/BlogCard.vue +++ b/resources/js/components/BlogCard.vue @@ -1,5 +1,6 @@ -
-
@@ -78,7 +79,6 @@ const formattedDate = computed(() => {
-

{

-

{{ blog.excerpt }}

+
- + + diff --git a/resources/js/components/BlogRow.vue b/resources/js/components/BlogRow.vue index 42b7826..36b7775 100644 --- a/resources/js/components/BlogRow.vue +++ b/resources/js/components/BlogRow.vue @@ -1,5 +1,7 @@ diff --git a/resources/js/components/BreadcrumbNav.vue b/resources/js/components/BreadcrumbNav.vue index a373c91..b2bb07f 100644 --- a/resources/js/components/BreadcrumbNav.vue +++ b/resources/js/components/BreadcrumbNav.vue @@ -25,7 +25,7 @@ const breadcrumbs = computed(() => { diff --git a/resources/js/layouts/AdminLayout.vue b/resources/js/layouts/AdminLayout.vue index 99fbab9..6b46fbd 100644 --- a/resources/js/layouts/AdminLayout.vue +++ b/resources/js/layouts/AdminLayout.vue @@ -11,6 +11,7 @@ import { import { ref } from 'vue'; import DesktopSidebar from '@/components/admin/DesktopSidebar.vue'; import MobileSideBar from '@/components/admin/MobileSideBar.vue'; +import KonstaProvider from '@/components/KonstaProvider.vue'; import LoadingSpinner from '@/components/LoadingSpinner.vue'; import NavBar from '@/components/NavBar.vue'; import ToastNotification from '@/components/ToastNotification.vue'; @@ -36,62 +37,66 @@ const closeMobileSidebar = () => { diff --git a/resources/js/layouts/AppLayout.vue b/resources/js/layouts/AppLayout.vue index c0639ae..35722ad 100644 --- a/resources/js/layouts/AppLayout.vue +++ b/resources/js/layouts/AppLayout.vue @@ -1,36 +1,41 @@ diff --git a/resources/js/types/global.d.ts b/resources/js/types/global.d.ts index a53aee2..6654ab8 100644 --- a/resources/js/types/global.d.ts +++ b/resources/js/types/global.d.ts @@ -31,3 +31,272 @@ declare module 'vue' { $headManager: ReturnType; } } + +declare module 'konsta/vue' { + import type { DefineComponent } from 'vue'; + + export const kApp: DefineComponent< + { + theme?: 'ios' | 'material'; + dark?: boolean; + touchRipple?: boolean; + }, + Record, + unknown + >; + export const kPage: DefineComponent< + Record, + Record, + unknown + >; + export const kNavbar: DefineComponent< + { + title?: string; + subtitle?: string; + backLink?: string | boolean; + transparent?: boolean; + blur?: boolean; + }, + Record, + unknown + >; + export const kBlock: DefineComponent< + { + strong?: boolean; + inset?: boolean; + }, + Record, + unknown + >; + export const kButton: DefineComponent< + { + text?: string; + icon?: string; + iconOnly?: boolean; + fill?: boolean; + outline?: boolean; + rounded?: boolean; + small?: boolean; + large?: boolean; + raised?: boolean; + disabled?: boolean; + }, + Record, + unknown + >; + export const kCard: DefineComponent< + { + header?: string; + footer?: string; + outline?: boolean; + expandable?: boolean; + }, + Record, + unknown + >; + export const kList: DefineComponent< + { + strong?: boolean; + inset?: boolean; + dividers?: boolean; + outline?: boolean; + }, + Record, + unknown + >; + export const kListItem: DefineComponent< + { + title?: string; + subtitle?: string; + text?: string; + media?: string; + after?: string; + link?: boolean | string; + header?: string; + footer?: string; + }, + Record, + unknown + >; + export const kDialog: DefineComponent< + { + opened?: boolean; + }, + Record, + unknown + >; + export const kToast: DefineComponent< + { + opened?: boolean; + position?: string; + }, + Record, + unknown + >; + export const kInput: DefineComponent< + { + type?: string; + value?: string | number; + placeholder?: string; + error?: string; + info?: string; + label?: string; + outline?: boolean; + disabled?: boolean; + }, + Record, + unknown + >; + export const kSelect: DefineComponent< + { + value?: string | number; + placeholder?: string; + label?: string; + outline?: boolean; + disabled?: boolean; + }, + Record, + unknown + >; + export const kTextarea: DefineComponent< + { + value?: string; + placeholder?: string; + label?: string; + outline?: boolean; + disabled?: boolean; + resizable?: boolean; + }, + Record, + unknown + >; + export const kToggle: DefineComponent< + { + checked?: boolean; + disabled?: boolean; + }, + Record, + unknown + >; + export const kCheckbox: DefineComponent< + { + checked?: boolean; + name?: string; + value?: string | number; + disabled?: boolean; + }, + Record, + unknown + >; + export const kBadge: DefineComponent< + { + color?: string; + }, + Record, + unknown + >; + export const kChip: DefineComponent< + { + text?: string; + delete?: boolean; + outline?: boolean; + }, + Record, + unknown + >; + export const kPreloader: DefineComponent< + { + size?: string; + color?: string; + }, + Record, + unknown + >; + export const kLink: DefineComponent< + { + text?: string; + icon?: string; + iconOnly?: boolean; + }, + Record, + unknown + >; + export const kPanel: DefineComponent< + { + opened?: boolean; + side?: 'left' | 'right'; + effect?: string; + }, + Record, + unknown + >; + export const kPopover: DefineComponent< + { + opened?: boolean; + target?: string; + }, + Record, + unknown + >; + export const kSearchbar: DefineComponent< + { + value?: string; + placeholder?: string; + disableButton?: boolean; + }, + Record, + unknown + >; + export const kBreadcrumbs: DefineComponent< + Record, + Record, + unknown + >; + export const kBreadcrumbsItem: DefineComponent< + { + active?: boolean; + }, + Record, + unknown + >; + export const kBlockTitle: DefineComponent< + { + medium?: boolean; + large?: boolean; + }, + Record, + unknown + >; + export const kSegmented: DefineComponent< + { + strong?: boolean; + outline?: boolean; + round?: boolean; + }, + Record, + unknown + >; + export const kTabbar: DefineComponent< + { + labels?: boolean; + outline?: boolean; + fill?: boolean; + translucent?: boolean; + position?: 'top' | 'bottom'; + }, + Record, + unknown + >; + export const kTabbarLink: DefineComponent< + { + text?: string; + icon?: string; + active?: boolean; + badge?: string | number; + }, + Record, + unknown + >; + + export function useTheme(): { value: 'ios' | 'material' }; +} From 5f44c9adddaa804dde7579b07a91e473f0ab2fd0 Mon Sep 17 00:00:00 2001 From: Tahsin Shahriar Date: Sun, 16 Aug 2026 21:41:34 +0600 Subject: [PATCH 03/13] feat: Konsta UI Phase 2 - admin components + form migration Phase 2a - Sidebars: - DesktopSidebar: kList + kListItem for nav items - MobileSideBar: kPanel for slide-out drawer Phase 2b - Confirm dialogs: - Replaced 6 native confirm() calls with kDialog - DesktopSidebar, MobileSideBar, SubjectCard, NodeRow, ResourceRow, UserRow Phase 2c - Forms (9 files): - SubjectCreateOrEdit, NodeCreateOrEdit, BlogCreateOrEdit - ResourceCreateOrEdit, NoticeEdit, Login - users/CreateOrEdit, BulkImageCreate, BulkVideoCreate - kInput, kSelect, kTextarea, kButton, kCheckbox, kToggle - File uploads kept as native input --- .../js/components/admin/DesktopSidebar.vue | 123 +++++--- .../js/components/admin/MobileSideBar.vue | 140 ++++++--- resources/js/components/admin/NodeRow.vue | 37 ++- resources/js/components/admin/ResourceRow.vue | 36 ++- resources/js/components/admin/SubjectCard.vue | 37 ++- resources/js/components/admin/UserRow.vue | 38 ++- resources/js/pages/admin/BlogCreateOrEdit.vue | 62 ++-- resources/js/pages/admin/NodeCreateOrEdit.vue | 55 ++-- resources/js/pages/admin/NoticeEdit.vue | 167 ++++------- .../js/pages/admin/ResourceCreateOrEdit.vue | 97 +++---- .../js/pages/admin/SubjectCreateOrEdit.vue | 93 +++--- .../pages/admin/resources/BulkImageCreate.vue | 41 +-- .../pages/admin/resources/BulkVideoCreate.vue | 75 ++--- .../js/pages/admin/users/CreateOrEdit.vue | 266 ++++++------------ resources/js/pages/auth/Login.vue | 49 ++-- 15 files changed, 656 insertions(+), 660 deletions(-) diff --git a/resources/js/components/admin/DesktopSidebar.vue b/resources/js/components/admin/DesktopSidebar.vue index 32ea2d8..e441328 100644 --- a/resources/js/components/admin/DesktopSidebar.vue +++ b/resources/js/components/admin/DesktopSidebar.vue @@ -1,21 +1,24 @@ @@ -27,39 +30,45 @@ const handleClearCache = () => { Management

-
- +
- - - - {{ item.name }} - - -
-
+ + + + + +
+
+ -
+
- + + + +
+

+ Confirm Logout +

+

+ Are you sure you want to log out? +

+
+ Cancel + Confirm +
+
+
+ + +
+

+ Clear Cache +

+

+ Are you sure you want to clear all cache? +

+
+ Cancel + Confirm +
+
+
diff --git a/resources/js/components/admin/MobileSideBar.vue b/resources/js/components/admin/MobileSideBar.vue index aa81223..1fd6671 100644 --- a/resources/js/components/admin/MobileSideBar.vue +++ b/resources/js/components/admin/MobileSideBar.vue @@ -1,27 +1,47 @@ diff --git a/resources/js/components/admin/NodeRow.vue b/resources/js/components/admin/NodeRow.vue index 359fed7..51cc639 100644 --- a/resources/js/components/admin/NodeRow.vue +++ b/resources/js/components/admin/NodeRow.vue @@ -1,15 +1,24 @@ @@ -33,7 +42,7 @@ const handleDelete = () => { + diff --git a/resources/js/pages/admin/NodeCreateOrEdit.vue b/resources/js/pages/admin/NodeCreateOrEdit.vue index 454e2ee..ef8243b 100644 --- a/resources/js/pages/admin/NodeCreateOrEdit.vue +++ b/resources/js/pages/admin/NodeCreateOrEdit.vue @@ -1,6 +1,7 @@ diff --git a/resources/js/pages/admin/BlogCreateOrEdit.vue b/resources/js/pages/admin/BlogCreateOrEdit.vue index 5ad8521..3e93629 100644 --- a/resources/js/pages/admin/BlogCreateOrEdit.vue +++ b/resources/js/pages/admin/BlogCreateOrEdit.vue @@ -2,7 +2,7 @@ import { Link, useForm } from '@inertiajs/vue3'; import { Upload } from 'lucide-vue-next'; import HTMLEditor from '@/components/HTMLEditor.vue'; -import { kInput, kCheckbox, kButton } from 'konsta/vue'; +import { kInput, kCheckbox, kButton, kBlockTitle } from 'konsta/vue'; const props = defineProps({ blog: { @@ -50,11 +50,11 @@ const submitForm = () => { class="mb-8 flex flex-col justify-between gap-4 border-b border-slate-100 pb-6 sm:flex-row sm:items-center dark:border-gray-800" >
-

{{ props.blog ? 'Edit' : 'Create' }} Blog Post -

+

Compose and manage article content for your web application audience. diff --git a/resources/js/pages/admin/Dashboard.vue b/resources/js/pages/admin/Dashboard.vue index 589f803..1c98c4f 100644 --- a/resources/js/pages/admin/Dashboard.vue +++ b/resources/js/pages/admin/Dashboard.vue @@ -9,6 +9,7 @@ import { BookOpen, ArrowRight, } from 'lucide-vue-next'; +import { kBlock, kBlockTitle, kBadge } from 'konsta/vue'; defineProps({ stats: Object, @@ -18,8 +19,8 @@ defineProps({ diff --git a/resources/js/pages/admin/Index.vue b/resources/js/pages/admin/Index.vue index 8d64633..f98edb1 100644 --- a/resources/js/pages/admin/Index.vue +++ b/resources/js/pages/admin/Index.vue @@ -3,6 +3,8 @@ import { Link } from '@inertiajs/vue3'; import { Plus } from 'lucide-vue-next'; import SubjectCard from '@/components/admin/SubjectCard.vue'; import EmptyState from '@/components/EmptyState.vue'; +import { kBlock, kBlockTitle, kBadge } from 'konsta/vue'; + defineProps({ subjects: { type: Array, @@ -12,33 +14,33 @@ defineProps({ diff --git a/resources/js/pages/admin/Node.vue b/resources/js/pages/admin/Node.vue index 6d45848..32b2eba 100644 --- a/resources/js/pages/admin/Node.vue +++ b/resources/js/pages/admin/Node.vue @@ -5,6 +5,7 @@ import { computed, ref, onMounted, onUnmounted } from 'vue'; import NodeRow from '@/components/admin/NodeRow.vue'; import ResourceRow from '@/components/admin/ResourceRow.vue'; import EmptyState from '@/components/EmptyState.vue'; +import { kBlock, kBlockTitle, kBadge } from 'konsta/vue'; const props = defineProps({ subject: Object, @@ -57,8 +58,8 @@ onUnmounted(() => document.removeEventListener('click', closeDropdown));