@vue-skuilder/cli 0.1.7 → 0.1.8-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.
Files changed (168) hide show
  1. package/README.md +122 -8
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +34 -3
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/pack.d.ts +10 -0
  10. package/dist/commands/pack.d.ts.map +1 -1
  11. package/dist/commands/pack.js +1 -1
  12. package/dist/commands/pack.js.map +1 -1
  13. package/dist/commands/studio.d.ts.map +1 -1
  14. package/dist/commands/studio.js +640 -105
  15. package/dist/commands/studio.js.map +1 -1
  16. package/dist/commands/unpack.d.ts +12 -0
  17. package/dist/commands/unpack.d.ts.map +1 -1
  18. package/dist/commands/unpack.js +1 -1
  19. package/dist/commands/unpack.js.map +1 -1
  20. package/dist/express-assets/app.d.ts +6 -0
  21. package/dist/express-assets/app.d.ts.map +1 -0
  22. package/dist/express-assets/app.js +209 -0
  23. package/dist/express-assets/app.js.map +1 -0
  24. package/dist/express-assets/assets/classroomDesignDoc.js +24 -0
  25. package/dist/express-assets/assets/courseValidateDocUpdate.js +56 -0
  26. package/dist/express-assets/assets/get-tagsDesignDoc.json +9 -0
  27. package/dist/express-assets/attachment-preprocessing/index.d.ts +11 -0
  28. package/dist/express-assets/attachment-preprocessing/index.d.ts.map +1 -0
  29. package/dist/express-assets/attachment-preprocessing/index.js +204 -0
  30. package/dist/express-assets/attachment-preprocessing/index.js.map +1 -0
  31. package/dist/express-assets/attachment-preprocessing/normalize.d.ts +7 -0
  32. package/dist/express-assets/attachment-preprocessing/normalize.d.ts.map +1 -0
  33. package/dist/express-assets/attachment-preprocessing/normalize.js +90 -0
  34. package/dist/express-assets/attachment-preprocessing/normalize.js.map +1 -0
  35. package/dist/express-assets/client-requests/classroom-requests.d.ts +26 -0
  36. package/dist/express-assets/client-requests/classroom-requests.d.ts.map +1 -0
  37. package/dist/express-assets/client-requests/classroom-requests.js +171 -0
  38. package/dist/express-assets/client-requests/classroom-requests.js.map +1 -0
  39. package/dist/express-assets/client-requests/course-requests.d.ts +10 -0
  40. package/dist/express-assets/client-requests/course-requests.d.ts.map +1 -0
  41. package/dist/express-assets/client-requests/course-requests.js +135 -0
  42. package/dist/express-assets/client-requests/course-requests.js.map +1 -0
  43. package/dist/express-assets/client-requests/pack-requests.d.ts +19 -0
  44. package/dist/express-assets/client-requests/pack-requests.d.ts.map +1 -0
  45. package/dist/express-assets/client-requests/pack-requests.js +130 -0
  46. package/dist/express-assets/client-requests/pack-requests.js.map +1 -0
  47. package/dist/express-assets/client.d.ts +31 -0
  48. package/dist/express-assets/client.d.ts.map +1 -0
  49. package/dist/express-assets/client.js +70 -0
  50. package/dist/express-assets/client.js.map +1 -0
  51. package/dist/express-assets/couchdb/authentication.d.ts +4 -0
  52. package/dist/express-assets/couchdb/authentication.d.ts.map +1 -0
  53. package/dist/express-assets/couchdb/authentication.js +69 -0
  54. package/dist/express-assets/couchdb/authentication.js.map +1 -0
  55. package/dist/express-assets/couchdb/index.d.ts +18 -0
  56. package/dist/express-assets/couchdb/index.d.ts.map +1 -0
  57. package/dist/express-assets/couchdb/index.js +52 -0
  58. package/dist/express-assets/couchdb/index.js.map +1 -0
  59. package/dist/express-assets/design-docs.d.ts +63 -0
  60. package/dist/express-assets/design-docs.d.ts.map +1 -0
  61. package/dist/express-assets/design-docs.js +90 -0
  62. package/dist/express-assets/design-docs.js.map +1 -0
  63. package/dist/express-assets/logger.d.ts +3 -0
  64. package/dist/express-assets/logger.d.ts.map +1 -0
  65. package/dist/express-assets/logger.js +62 -0
  66. package/dist/express-assets/logger.js.map +1 -0
  67. package/dist/express-assets/routes/logs.d.ts +3 -0
  68. package/dist/express-assets/routes/logs.d.ts.map +1 -0
  69. package/dist/express-assets/routes/logs.js +274 -0
  70. package/dist/express-assets/routes/logs.js.map +1 -0
  71. package/dist/express-assets/utils/env.d.ts +11 -0
  72. package/dist/express-assets/utils/env.d.ts.map +1 -0
  73. package/dist/express-assets/utils/env.js +39 -0
  74. package/dist/express-assets/utils/env.js.map +1 -0
  75. package/dist/express-assets/utils/processQueue.d.ts +39 -0
  76. package/dist/express-assets/utils/processQueue.d.ts.map +1 -0
  77. package/dist/express-assets/utils/processQueue.js +175 -0
  78. package/dist/express-assets/utils/processQueue.js.map +1 -0
  79. package/dist/studio-ui-src/App.vue +132 -0
  80. package/dist/studio-ui-src/api/index.ts +30 -0
  81. package/dist/studio-ui-src/components/StudioFlush.vue +108 -0
  82. package/dist/studio-ui-src/config/development.ts +98 -0
  83. package/dist/studio-ui-src/index.html +13 -0
  84. package/dist/studio-ui-src/main.ts +148 -0
  85. package/dist/studio-ui-src/package.json +35 -0
  86. package/dist/studio-ui-src/router/index.ts +32 -0
  87. package/dist/studio-ui-src/stores/useAuthStore.ts +3 -0
  88. package/dist/studio-ui-src/tsconfig.json +28 -0
  89. package/dist/studio-ui-src/views/BrowseView.vue +82 -0
  90. package/dist/studio-ui-src/views/BulkImportView.vue +89 -0
  91. package/dist/studio-ui-src/views/CourseEditorView.vue +62 -0
  92. package/dist/studio-ui-src/views/CreateCardView.vue +140 -0
  93. package/dist/studio-ui-src/vite.config.base.js +100 -0
  94. package/dist/studio-ui-src/vite.config.ts +26 -0
  95. package/dist/templates/.skuilder/README.md +29 -0
  96. package/dist/types.d.ts +5 -0
  97. package/dist/types.d.ts.map +1 -1
  98. package/dist/types.js.map +1 -1
  99. package/dist/utils/ExpressManager.d.ts +28 -0
  100. package/dist/utils/ExpressManager.d.ts.map +1 -0
  101. package/dist/utils/ExpressManager.js +166 -0
  102. package/dist/utils/ExpressManager.js.map +1 -0
  103. package/dist/utils/NodeFileSystemAdapter.d.ts +6 -0
  104. package/dist/utils/NodeFileSystemAdapter.d.ts.map +1 -1
  105. package/dist/utils/NodeFileSystemAdapter.js +29 -1
  106. package/dist/utils/NodeFileSystemAdapter.js.map +1 -1
  107. package/dist/utils/error-reporting.d.ts +54 -0
  108. package/dist/utils/error-reporting.d.ts.map +1 -0
  109. package/dist/utils/error-reporting.js +143 -0
  110. package/dist/utils/error-reporting.js.map +1 -0
  111. package/dist/utils/pack-courses.d.ts.map +1 -1
  112. package/dist/utils/pack-courses.js +10 -27
  113. package/dist/utils/pack-courses.js.map +1 -1
  114. package/dist/utils/prompts.d.ts.map +1 -1
  115. package/dist/utils/prompts.js +24 -0
  116. package/dist/utils/prompts.js.map +1 -1
  117. package/dist/utils/questions-hash.d.ts +22 -0
  118. package/dist/utils/questions-hash.d.ts.map +1 -0
  119. package/dist/utils/questions-hash.js +96 -0
  120. package/dist/utils/questions-hash.js.map +1 -0
  121. package/dist/utils/template.d.ts +1 -1
  122. package/dist/utils/template.d.ts.map +1 -1
  123. package/dist/utils/template.js +202 -25
  124. package/dist/utils/template.js.map +1 -1
  125. package/eslint.config.mjs +1 -1
  126. package/package.json +30 -11
  127. package/src/cli.ts +3 -1
  128. package/src/commands/init.ts +48 -3
  129. package/src/commands/pack.ts +1 -1
  130. package/src/commands/studio.ts +850 -121
  131. package/src/commands/unpack.ts +1 -1
  132. package/src/types.ts +5 -0
  133. package/src/utils/ExpressManager.ts +210 -0
  134. package/src/utils/NodeFileSystemAdapter.ts +46 -2
  135. package/src/utils/error-reporting.ts +192 -0
  136. package/src/utils/pack-courses.ts +11 -36
  137. package/src/utils/prompts.ts +34 -0
  138. package/src/utils/questions-hash.ts +109 -0
  139. package/src/utils/template.ts +231 -27
  140. package/templates/.skuilder/README.md +29 -0
  141. package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js +0 -2
  142. package/dist/studio-ui-assets/assets/BrowseView-BJbixGOU.js.map +0 -1
  143. package/dist/studio-ui-assets/assets/BrowseView-CM4HBO4j.css +0 -1
  144. package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js +0 -2
  145. package/dist/studio-ui-assets/assets/BulkImportView-DB6DYDJU.js.map +0 -1
  146. package/dist/studio-ui-assets/assets/BulkImportView-g4wQUfPA.css +0 -1
  147. package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js +0 -2
  148. package/dist/studio-ui-assets/assets/CourseEditorView-BIlhlhw1.js.map +0 -1
  149. package/dist/studio-ui-assets/assets/CourseEditorView-WuPNLVKp.css +0 -1
  150. package/dist/studio-ui-assets/assets/CreateCardView-CyNOKCkm.css +0 -1
  151. package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js +0 -2
  152. package/dist/studio-ui-assets/assets/CreateCardView-DPjPvzzt.js.map +0 -1
  153. package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js +0 -330
  154. package/dist/studio-ui-assets/assets/edit-ui.es-DiUxqbgF.js.map +0 -1
  155. package/dist/studio-ui-assets/assets/index--zY88pg6.css +0 -14
  156. package/dist/studio-ui-assets/assets/index-BnAv1C72.js +0 -287
  157. package/dist/studio-ui-assets/assets/index-BnAv1C72.js.map +0 -1
  158. package/dist/studio-ui-assets/assets/index-DHMXQY3-.js +0 -192
  159. package/dist/studio-ui-assets/assets/index-DHMXQY3-.js.map +0 -1
  160. package/dist/studio-ui-assets/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
  161. package/dist/studio-ui-assets/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
  162. package/dist/studio-ui-assets/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
  163. package/dist/studio-ui-assets/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
  164. package/dist/studio-ui-assets/assets/vue-DZcMATiC.js +0 -28
  165. package/dist/studio-ui-assets/assets/vue-DZcMATiC.js.map +0 -1
  166. package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js +0 -6
  167. package/dist/studio-ui-assets/assets/vuetify-qg7mRxy_.js.map +0 -1
  168. package/dist/studio-ui-assets/index.html +0 -16
@@ -0,0 +1,148 @@
1
+ import { createApp } from 'vue';
2
+ import { createPinia } from 'pinia';
3
+
4
+ // Vuetify
5
+ import 'vuetify/styles';
6
+ import { createVuetify } from 'vuetify';
7
+ import * as components from 'vuetify/components';
8
+ import * as directives from 'vuetify/directives';
9
+ import '@mdi/font/css/materialdesignicons.css';
10
+
11
+ // Component library styles
12
+ import '@vue-skuilder/courses/style';
13
+ import '@vue-skuilder/common-ui/style';
14
+ import '@vue-skuilder/edit-ui/style';
15
+
16
+ // Data layer initialization
17
+ import { initializeDataLayer } from '@vue-skuilder/db';
18
+
19
+ import App from './App.vue';
20
+ import router from './router';
21
+ import { getStudioConfig, getConfigErrorMessage } from './config/development';
22
+
23
+ // Initialize Vuetify with all components and directives
24
+ const vuetify = createVuetify({
25
+ components,
26
+ directives,
27
+ theme: {
28
+ defaultTheme: 'light',
29
+ },
30
+ });
31
+
32
+ (async () => {
33
+ // Get studio configuration (CLI-injected or environment variables)
34
+ const studioConfig = getStudioConfig();
35
+
36
+ if (!studioConfig) {
37
+ throw new Error(getConfigErrorMessage());
38
+ }
39
+
40
+ // Parse the CLI-provided CouchDB URL (format: http://localhost:5985)
41
+ const couchUrl = new URL(studioConfig.couchdb.url);
42
+ const serverUrl = `${couchUrl.hostname}:${couchUrl.port}/`;
43
+
44
+ console.log('🎨 Studio Mode: Initializing data layer with CLI-provided CouchDB connection');
45
+ console.log(` Server: ${couchUrl.protocol}//${serverUrl}`);
46
+ console.log(` Username: ${studioConfig.couchdb.username}`);
47
+
48
+ await initializeDataLayer({
49
+ type: 'couch',
50
+ options: {
51
+ COUCHDB_SERVER_PROTOCOL: couchUrl.protocol.replace(':', ''),
52
+ COUCHDB_SERVER_URL: serverUrl,
53
+ COUCHDB_USERNAME: studioConfig.couchdb.username,
54
+ COUCHDB_PASSWORD: studioConfig.couchdb.password,
55
+ },
56
+ });
57
+
58
+ const pinia = createPinia();
59
+ const app = createApp(App);
60
+
61
+ // Register all course view components globally (like platform-ui)
62
+ console.log('🎨 Studio Mode: Importing course view components');
63
+ const { allCourses: Courses } = await import('@vue-skuilder/courses');
64
+ console.log(' ✅ Course components imported successfully');
65
+
66
+ // Check for custom questions configuration and import if available
67
+ console.log('🎨 Studio Mode: Checking for custom questions configuration');
68
+ let customQuestions = null;
69
+ try {
70
+ console.log(' 📁 Fetching custom-questions-config.json');
71
+ const configResponse = await fetch('/custom-questions-config.json');
72
+ if (configResponse.ok) {
73
+ console.log(' ✅ Custom questions config file found');
74
+ const customConfig = await configResponse.json();
75
+ console.log(' 📋 Custom config parsed:', customConfig);
76
+ if (customConfig.hasCustomQuestions && customConfig.importPath) {
77
+ console.log(`🎨 Studio Mode: Loading custom questions from ${customConfig.packageName}`);
78
+ console.log(` 📦 Import path: ${customConfig.importPath}`);
79
+ try {
80
+ const customModule = await import(customConfig.importPath);
81
+ console.log(' ✅ Custom module imported successfully');
82
+ customQuestions = customModule.allCustomQuestions?.();
83
+ if (customQuestions) {
84
+ console.log(
85
+ ` ✅ Loaded custom questions: ${customQuestions.questionClasses?.length || 0} types`
86
+ );
87
+ console.log(' 📊 Custom questions object:', customQuestions);
88
+ } else {
89
+ console.log(' ⚠️ Custom module did not return questions data');
90
+ }
91
+ } catch (importError) {
92
+ console.warn(
93
+ ` ⚠️ Failed to import custom questions: ${importError instanceof Error ? importError.message : String(importError)}`
94
+ );
95
+ }
96
+ } else {
97
+ console.log(
98
+ ' ℹ️ Custom config exists but hasCustomQuestions is false or importPath is missing'
99
+ );
100
+ }
101
+ } else {
102
+ console.log(' ℹ️ Custom questions config file not found (this is normal)');
103
+ }
104
+ } catch (configError) {
105
+ // No custom questions config - this is normal for default studio mode
106
+ console.log(' ℹ️ No custom questions config available (default studio mode)');
107
+ }
108
+
109
+
110
+ console.log('🎨 Studio Mode: Collecting view components');
111
+ const viewComponents = Courses.allViewsRaw();
112
+ console.log(` ✅ Collected ${Object.keys(viewComponents).length} base view components`);
113
+
114
+ // Add custom question view components if available
115
+ if (customQuestions?.views) {
116
+ console.log(` 📦 Adding ${customQuestions.views.length} custom question view components`);
117
+ customQuestions.views.forEach((view: any) => {
118
+ if (view.name && view.component) {
119
+ console.log(` ➕ Registering custom view component: ${view.name}`);
120
+ viewComponents[view.name] = view.component;
121
+ } else {
122
+ console.warn(` ⚠️ Skipping invalid custom view (missing name or component):`, view);
123
+ }
124
+ });
125
+ console.log(
126
+ ` ✅ Total view components after custom additions: ${Object.keys(viewComponents).length}`
127
+ );
128
+ } else {
129
+ console.log(' ℹ️ No custom question views to add');
130
+ }
131
+
132
+ Object.entries(viewComponents).forEach(([name, component]) => {
133
+ app.component(name, component);
134
+ });
135
+
136
+ app.use(pinia);
137
+ app.use(vuetify);
138
+ app.use(router);
139
+
140
+ // Dynamically import piniaPlugin (like platform-ui)
141
+ const { piniaPlugin, useAuthStore } = await import('@vue-skuilder/common-ui');
142
+ app.use(piniaPlugin, { pinia });
143
+
144
+ // Initialize auth store like standalone-ui does
145
+ await useAuthStore().init();
146
+
147
+ app.mount('#app');
148
+ })();
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@vue-skuilder/studio-ui",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.8-0",
7
+ "type": "module",
8
+ "scripts": {
9
+ "dev": "vite",
10
+ "build": "vite build",
11
+ "preview": "vite preview",
12
+ "lint": "eslint . --fix",
13
+ "lint:check": "eslint ."
14
+ },
15
+ "dependencies": {
16
+ "@mdi/font": "^7.3.67",
17
+ "@vue-skuilder/common": "workspace:*",
18
+ "@vue-skuilder/common-ui": "workspace:*",
19
+ "@vue-skuilder/courses": "workspace:*",
20
+ "@vue-skuilder/db": "workspace:*",
21
+ "@vue-skuilder/edit-ui": "workspace:*",
22
+ "pinia": "^2.3.0",
23
+ "vue": "^3.5.13",
24
+ "vue-router": "^4.2.0",
25
+ "vuetify": "^3.7.0"
26
+ },
27
+ "devDependencies": {
28
+ "@vitejs/plugin-vue": "^5.2.1",
29
+ "eslint": "^9.30.1",
30
+ "typescript": "^5.7.2",
31
+ "vite": "^6.0.9",
32
+ "vue-tsc": "^1.8.0"
33
+ },
34
+ "stableVersion": "0.1.7"
35
+ }
@@ -0,0 +1,32 @@
1
+ import { createRouter, createWebHistory } from 'vue-router';
2
+ import type { RouteRecordRaw } from 'vue-router';
3
+
4
+ const routes: RouteRecordRaw[] = [
5
+ {
6
+ path: '/',
7
+ name: 'browse',
8
+ component: () => import('../views/BrowseView.vue'),
9
+ },
10
+ {
11
+ path: '/course-editor',
12
+ name: 'course-editor',
13
+ component: () => import('../views/CourseEditorView.vue'),
14
+ },
15
+ {
16
+ path: '/bulk-import',
17
+ name: 'bulk-import',
18
+ component: () => import('../views/BulkImportView.vue'),
19
+ },
20
+ {
21
+ path: '/create-card',
22
+ name: 'create-card',
23
+ component: () => import('../views/CreateCardView.vue'),
24
+ },
25
+ ];
26
+
27
+ const router = createRouter({
28
+ history: createWebHistory(),
29
+ routes,
30
+ });
31
+
32
+ export default router;
@@ -0,0 +1,3 @@
1
+ // Studio-ui uses the common-ui auth store directly
2
+ // This file can be removed once we confirm the integration works
3
+ export { useAuthStore, getCurrentUser } from '@vue-skuilder/common-ui';
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "preserve",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+
23
+ /* Node config (for vite.config.ts) */
24
+ "composite": true,
25
+ "allowSyntheticDefaultImports": true
26
+ },
27
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "vite.config.ts"]
28
+ }
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="browse-view">
3
+ <div v-if="loading" class="text-center pa-4">
4
+ <v-progress-circular indeterminate />
5
+ <p class="mt-2">Loading course...</p>
6
+ </div>
7
+
8
+ <div v-else-if="error" class="text-center pa-4">
9
+ <v-icon color="error" size="48">mdi-alert-circle</v-icon>
10
+ <h2 class="mt-2">Browse Error</h2>
11
+ <p>{{ error }}</p>
12
+ </div>
13
+
14
+ <div v-else-if="courseId">
15
+ <course-information :course-id="courseId" :view-lookup-function="viewLookupFunction" :edit-mode="'full'">
16
+ <template #actions>&nbsp;</template>
17
+ </course-information>
18
+ </div>
19
+
20
+ <div v-else class="text-center pa-4">
21
+ <v-icon size="48">mdi-school</v-icon>
22
+ <h2 class="mt-2">No Course Loaded</h2>
23
+ <p>Please load a course to start browsing.</p>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import { ref, onMounted } from 'vue';
30
+ import { CourseInformation } from '@vue-skuilder/common-ui';
31
+ import { allCourses } from '@vue-skuilder/courses';
32
+ import { getStudioConfig, getConfigErrorMessage } from '../config/development';
33
+
34
+ // Browse view state
35
+ const loading = ref(true);
36
+ const error = ref<string | null>(null);
37
+ const courseId = ref<string | null>(null);
38
+
39
+ // View lookup function with proper context binding
40
+ const viewLookupFunction = (viewDescription: any) => {
41
+ return allCourses.getView(viewDescription);
42
+ };
43
+
44
+ // Initialize browse view
45
+ onMounted(async () => {
46
+ try {
47
+ // Get studio configuration (CLI-injected or environment variables)
48
+ const studioConfig = getStudioConfig();
49
+
50
+ if (!studioConfig) {
51
+ throw new Error(getConfigErrorMessage());
52
+ }
53
+
54
+ courseId.value = studioConfig.database.name;
55
+ loading.value = false;
56
+ } catch (err) {
57
+ console.error('Browse view initialization error:', err);
58
+ error.value = err instanceof Error ? err.message : 'Unknown error';
59
+ loading.value = false;
60
+ }
61
+ });
62
+ </script>
63
+
64
+ <style scoped>
65
+ .browse-view {
66
+ height: 100%;
67
+ }
68
+
69
+ .studio-header {
70
+ padding: 16px 0;
71
+ }
72
+
73
+ .studio-header h1 {
74
+ color: rgb(var(--v-theme-primary));
75
+ font-weight: 500;
76
+ }
77
+
78
+ .studio-header p {
79
+ color: rgb(var(--v-theme-on-surface-variant));
80
+ margin: 0;
81
+ }
82
+ </style>
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="bulk-import-view">
3
+ <v-card>
4
+ <v-card-title>
5
+ <v-icon start>mdi-file-import</v-icon>
6
+ Bulk Import Cards
7
+ </v-card-title>
8
+
9
+ <v-card-text>
10
+ <div v-if="loading" class="text-center pa-4">
11
+ <v-progress-circular indeterminate />
12
+ <p class="mt-2">Loading bulk import tool...</p>
13
+ </div>
14
+
15
+ <div v-else-if="error" class="text-center pa-4">
16
+ <v-icon color="error" size="24">mdi-alert-circle</v-icon>
17
+ <p class="mt-2 text-error">{{ error }}</p>
18
+ </div>
19
+
20
+ <div v-else-if="courseId && courseConfig">
21
+ <!-- Bulk Import View from edit-ui package -->
22
+ <bulk-import-view
23
+ :course-cfg="courseConfig"
24
+ :view-lookup-function="allCourses.getView"
25
+ @import-completed="onImportCompleted"
26
+ />
27
+ </div>
28
+
29
+ <div v-else>
30
+ <p class="text-center">No course loaded</p>
31
+ </div>
32
+ </v-card-text>
33
+ </v-card>
34
+ </div>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { ref, onMounted } from 'vue';
39
+ import { BulkImportView } from '@vue-skuilder/edit-ui';
40
+ import { allCourses } from '@vue-skuilder/courses';
41
+ import { getStudioConfig, getConfigErrorMessage } from '../config/development';
42
+ import { getDataLayer } from '@vue-skuilder/db';
43
+ import type { CourseConfig } from '@vue-skuilder/common';
44
+
45
+ // Bulk import state
46
+ const loading = ref(true);
47
+ const error = ref<string | null>(null);
48
+ const courseId = ref<string | null>(null);
49
+ const courseConfig = ref<CourseConfig | null>(null);
50
+
51
+ // Initialize bulk import view
52
+ onMounted(async () => {
53
+ try {
54
+ // Get studio configuration (CLI-injected or environment variables)
55
+ const studioConfig = getStudioConfig();
56
+
57
+ if (!studioConfig) {
58
+ throw new Error(getConfigErrorMessage());
59
+ }
60
+
61
+ courseId.value = studioConfig.database.name;
62
+
63
+ // Load course configuration
64
+ const dataLayer = getDataLayer();
65
+ const courseDB = dataLayer.getCourseDB(courseId.value);
66
+ courseConfig.value = await courseDB.getCourseConfig();
67
+
68
+ loading.value = false;
69
+ } catch (err) {
70
+ console.error('Bulk import initialization error:', err);
71
+ error.value = err instanceof Error ? err.message : 'Unknown error';
72
+ loading.value = false;
73
+ }
74
+ });
75
+
76
+ // Handle import completion
77
+ const onImportCompleted = (result: unknown) => {
78
+ console.log('Import completed:', result);
79
+ // Could add success notification here
80
+ };
81
+ </script>
82
+
83
+ <style scoped>
84
+ .bulk-import-view {
85
+ max-width: 1200px;
86
+ margin: 0 auto;
87
+ padding: 16px;
88
+ }
89
+ </style>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <div class="course-editor-view">
3
+ <div v-if="loading" class="text-center pa-4">
4
+ <v-progress-circular indeterminate />
5
+ <p class="mt-2">Loading course editor...</p>
6
+ </div>
7
+
8
+ <div v-else-if="error" class="text-center pa-4">
9
+ <v-icon color="error" size="48">mdi-alert-circle</v-icon>
10
+ <h2 class="mt-2">Editor Error</h2>
11
+ <p>{{ error }}</p>
12
+ </div>
13
+
14
+ <div v-else-if="courseId">
15
+ <!-- Course Editor from edit-ui package -->
16
+ <course-editor :course="courseId" :view-lookup-function="allCourses.getView" />
17
+ </div>
18
+
19
+ <div v-else class="text-center pa-4">
20
+ <v-icon size="48">mdi-school</v-icon>
21
+ <h2 class="mt-2">No Course Loaded</h2>
22
+ <p>Please load a course to start editing.</p>
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { ref, onMounted } from 'vue';
29
+ import { CourseEditor } from '@vue-skuilder/edit-ui';
30
+ import { allCourses } from '@vue-skuilder/courses';
31
+ import { getStudioConfig, getConfigErrorMessage } from '../config/development';
32
+
33
+ // Course editor state
34
+ const loading = ref(true);
35
+ const error = ref<string | null>(null);
36
+ const courseId = ref<string | null>(null);
37
+
38
+ // Initialize course editor
39
+ onMounted(async () => {
40
+ try {
41
+ // Get studio configuration (CLI-injected or environment variables)
42
+ const studioConfig = getStudioConfig();
43
+
44
+ if (!studioConfig) {
45
+ throw new Error(getConfigErrorMessage());
46
+ }
47
+
48
+ courseId.value = studioConfig.database.name;
49
+ loading.value = false;
50
+ } catch (err) {
51
+ console.error('Course editor initialization error:', err);
52
+ error.value = err instanceof Error ? err.message : 'Unknown error';
53
+ loading.value = false;
54
+ }
55
+ });
56
+ </script>
57
+
58
+ <style scoped>
59
+ .course-editor-view {
60
+ height: 100%;
61
+ }
62
+ </style>
@@ -0,0 +1,140 @@
1
+ <template>
2
+ <div class="create-card-view">
3
+ <v-card>
4
+ <v-card-title>
5
+ <v-icon start>mdi-card-plus</v-icon>
6
+ Create New Card
7
+ </v-card-title>
8
+
9
+ <v-card-text>
10
+ <div v-if="loading" class="text-center pa-4">
11
+ <v-progress-circular indeterminate />
12
+ <p class="mt-2">Loading card creation form...</p>
13
+ </div>
14
+
15
+ <div v-else-if="error" class="text-center pa-4">
16
+ <v-icon color="error" size="24">mdi-alert-circle</v-icon>
17
+ <p class="mt-2 text-error">{{ error }}</p>
18
+ </div>
19
+
20
+ <div v-else-if="courseId && courseConfig">
21
+ <!-- Card type selector -->
22
+ <v-select
23
+ v-if="availableDataShapes.length > 1"
24
+ v-model="selectedDataShapeIndex"
25
+ :items="
26
+ availableDataShapes.map((shape, index) => ({
27
+ title: shape.name.replace(/^.*\./, ''),
28
+ value: index,
29
+ }))
30
+ "
31
+ label="Card Type"
32
+ class="mb-4"
33
+ />
34
+
35
+ <!-- Data Input Form from edit-ui package -->
36
+ <data-input-form
37
+ v-if="selectedDataShape"
38
+ :course-id="courseId"
39
+ :course-cfg="courseConfig"
40
+ :data-shape="selectedDataShape"
41
+ :view-lookup-function="allCourses.getView"
42
+ @card-created="onCardCreated"
43
+ />
44
+
45
+ <div v-else-if="availableDataShapes.length === 0" class="text-center pa-4">
46
+ <v-icon color="warning" size="24">mdi-alert</v-icon>
47
+ <p class="mt-2">No card types available in this course</p>
48
+ </div>
49
+ </div>
50
+
51
+ <div v-else>
52
+ <p class="text-center">No course loaded</p>
53
+ </div>
54
+ </v-card-text>
55
+ </v-card>
56
+ </div>
57
+ </template>
58
+
59
+ <script setup lang="ts">
60
+ import { ref, onMounted, computed } from 'vue';
61
+ import { DataInputForm } from '@vue-skuilder/edit-ui';
62
+ import { allCourses } from '@vue-skuilder/courses';
63
+ import { getStudioConfig, getConfigErrorMessage } from '../config/development';
64
+ import { getDataLayer } from '@vue-skuilder/db';
65
+ import type { CourseConfig, DataShape } from '@vue-skuilder/common';
66
+
67
+ // Create card state
68
+ const loading = ref(true);
69
+ const error = ref<string | null>(null);
70
+ const courseId = ref<string | null>(null);
71
+ const courseConfig = ref<CourseConfig | null>(null);
72
+ const selectedDataShapeIndex = ref<number>(0);
73
+
74
+ // Get available data shapes
75
+ const availableDataShapes = computed(() => {
76
+ if (!courseConfig.value?.dataShapes) return [];
77
+ return courseConfig.value.dataShapes;
78
+ });
79
+
80
+ // Get currently selected data shape
81
+ const selectedDataShape = computed((): DataShape | null => {
82
+ const shapes = availableDataShapes.value;
83
+ if (shapes.length === 0) return null;
84
+
85
+ // Find the corresponding DataShape from allCourses
86
+ const shapeName = shapes[selectedDataShapeIndex.value]?.name;
87
+ if (!shapeName) return null;
88
+
89
+ // Search through all courses to find the DataShape
90
+ for (const course of allCourses.courses) {
91
+ for (const question of course.questions) {
92
+ for (const dataShape of question.dataShapes) {
93
+ if (dataShape.name === shapeName.split('.').pop()) {
94
+ return dataShape;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return null;
100
+ });
101
+
102
+ // Initialize create card view
103
+ onMounted(async () => {
104
+ try {
105
+ // Get studio configuration (CLI-injected or environment variables)
106
+ const studioConfig = getStudioConfig();
107
+
108
+ if (!studioConfig) {
109
+ throw new Error(getConfigErrorMessage());
110
+ }
111
+
112
+ courseId.value = studioConfig.database.name;
113
+
114
+ // Load course configuration
115
+ const dataLayer = getDataLayer();
116
+ const courseDB = dataLayer.getCourseDB(courseId.value);
117
+ courseConfig.value = await courseDB.getCourseConfig();
118
+
119
+ loading.value = false;
120
+ } catch (err) {
121
+ console.error('Create card initialization error:', err);
122
+ error.value = err instanceof Error ? err.message : 'Unknown error';
123
+ loading.value = false;
124
+ }
125
+ });
126
+
127
+ // Handle card creation
128
+ const onCardCreated = (cardData: unknown) => {
129
+ console.log('Card created:', cardData);
130
+ // Could add success notification or redirect here
131
+ };
132
+ </script>
133
+
134
+ <style scoped>
135
+ .create-card-view {
136
+ max-width: 1200px;
137
+ margin: 0 auto;
138
+ padding: 16px;
139
+ }
140
+ </style>