diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 51598a7..6d13baa 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -16,6 +16,7 @@
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 217e5c5..f8467b4 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6b88eae..b5eaa1f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,7 +11,7 @@ mpsGradlePlugin = "1.7.288.4ea765f"
# modelix
modelixCore = "3.4.0"
-mpsModelServerSyncPlugin = "2021.2.145"
+mpsModelServerSyncPlugin = "2021.2.157"
# backend 1 / SPA
ktor = "2.3.4"
diff --git a/spa-collaboration-vue/.eslintrc.js b/spa-collaboration-vue/.eslintrc.js
index 5f3949b..d7362ba 100644
--- a/spa-collaboration-vue/.eslintrc.js
+++ b/spa-collaboration-vue/.eslintrc.js
@@ -10,5 +10,8 @@ module.exports = {
],
rules: {
'vue/multi-word-component-names': 'off',
+ "vue/valid-v-slot": ["error", {
+ "allowModifiers": false
+ }]
},
}
diff --git a/spa-collaboration-vue/TODO.md b/spa-collaboration-vue/TODO.md
index 087f0bf..1c8a07a 100644
--- a/spa-collaboration-vue/TODO.md
+++ b/spa-collaboration-vue/TODO.md
@@ -4,12 +4,19 @@
* /Users/odzhychko/Documents/arbeit1/modelix/modelix.samples/mps/metamodel-api-ts/package.json
* Set not snapshot version as dependencie for ts-model-api
* Set ts-model-api as peerDependencie
+
+# Visuell/Aufgabe
+* Plus an room list/lecture list/tutor list
+* In Weekly zeigen
+* Edit HOME vue in http://localhost:3000/
+* Make title redirect to http://localhost:3000/
+* IMPORTANT For demo Document weird syncing behaviour, need to remove and then add binding again (or fix bug)
* Add Icons in Webpage
* Change Favicon of page
* Change Title of page
-* Add Name to the varouis list types
* Show loading error in banner
* Reduce maximum widht of data tables
-* Implement Table for lectures and tutors
- * Resolve bug in references
-* Document weird syncing behaviour, need to remove and then add binding again (or fix bug)
\ No newline at end of file
+* rename collaboration to managment
+* /Users/odzhychko/Documents/arbeit1/modelix/modelix.samples/mps/metamodel-api-ts/package.json
+* Set not snapshot version as dependencie for ts-model-api
+* Set ts-model-api as peerDependencie
\ No newline at end of file
diff --git a/spa-collaboration-vue/package-lock.json b/spa-collaboration-vue/package-lock.json
index e5e0b96..56af7a7 100644
--- a/spa-collaboration-vue/package-lock.json
+++ b/spa-collaboration-vue/package-lock.json
@@ -11,6 +11,7 @@
"@mdi/font": "7.0.96",
"@modelix/vue-model-api": "^0.0.1-a578470.dirty-SNAPSHOT",
"core-js": "^3.29.0",
+ "date-fns": "^2.30.0",
"metamodel-api-ts": "file:../mps/metamodel-api-ts/build/packages/metamodel-api-ts-1.0.0.tgz",
"roboto-fontface": "*",
"vue": "^3.2.0",
@@ -651,6 +652,17 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.23.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
+ "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
@@ -2987,6 +2999,21 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
"node_modules/de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
@@ -5252,6 +5279,11 @@
"node": ">=8.10.0"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
diff --git a/spa-collaboration-vue/package.json b/spa-collaboration-vue/package.json
index 594a0a8..2780332 100644
--- a/spa-collaboration-vue/package.json
+++ b/spa-collaboration-vue/package.json
@@ -12,6 +12,7 @@
"@mdi/font": "7.0.96",
"@modelix/vue-model-api": "^0.0.1-a578470.dirty-SNAPSHOT",
"core-js": "^3.29.0",
+ "date-fns": "^2.30.0",
"metamodel-api-ts": "file:../mps/metamodel-api-ts/build/packages/metamodel-api-ts-1.0.0.tgz",
"roboto-fontface": "*",
"vue": "^3.2.0",
diff --git a/spa-collaboration-vue/src/App.vue b/spa-collaboration-vue/src/App.vue
index 90529cf..de9c4c0 100644
--- a/spa-collaboration-vue/src/App.vue
+++ b/spa-collaboration-vue/src/App.vue
@@ -8,7 +8,8 @@ import { useModelClient, useRootNode } from "@modelix/vue-model-api";
import { registerLanguages } from "metamodel-api-ts";
import { N_Module } from "metamodel-api-ts/build/dist/L_org_modelix_model_repositoryconcepts";
import { Ref, computed, provide } from "vue";
-import { LOADING_ERROR, MODEL } from "@/InjectionKeys";
+import { LOADING_ERROR, MODEL, ROOMS } from "@/InjectionKeys";
+import { isOfConcept_RoomList } from "metamodel-api-ts/build/dist/L_University_Schedule";
registerLanguages();
@@ -34,7 +35,7 @@ const loadingError: Ref = computed(() => {
return null;
});
-const module = computed(() => {
+const model = computed(() => {
if (rootNode.value == null) {
return null;
}
@@ -43,10 +44,16 @@ const module = computed(() => {
untypedModuleNode,
) as N_Module;
const model = module.models.asArray()[0];
- console.log(model)
return model;
});
-provide(MODEL, module);
+const rooms = computed(() => {
+ const rootNodes = model.value?.rootNodes.asArray() ?? [];
+ const roomLists = rootNodes.filter(isOfConcept_RoomList)
+ return roomLists.flatMap(roomList => roomList.rooms.asArray())
+})
+
+provide(MODEL, model);
+provide(ROOMS, rooms);
provide(LOADING_ERROR, loadingError);
diff --git a/spa-collaboration-vue/src/InjectionKeys.ts b/spa-collaboration-vue/src/InjectionKeys.ts
index 6eb17b0..3e545bb 100644
--- a/spa-collaboration-vue/src/InjectionKeys.ts
+++ b/spa-collaboration-vue/src/InjectionKeys.ts
@@ -1,5 +1,7 @@
import { InjectionKey, Ref } from 'vue';
import { N_Model } from 'metamodel-api-ts/build/dist/L_org_modelix_model_repositoryconcepts'
+import { N_Room } from 'metamodel-api-ts/build/dist/L_University_Schedule';
export const MODEL: InjectionKey>> = Symbol("MODEL")
+export const ROOMS: InjectionKey>> = Symbol("ROOMS")
export const LOADING_ERROR: InjectionKey>> = Symbol("LOADING_ERROR")
diff --git a/spa-collaboration-vue/src/components/LectureTable.vue b/spa-collaboration-vue/src/components/LectureTable.vue
new file mode 100644
index 0000000..886f0e2
--- /dev/null
+++ b/spa-collaboration-vue/src/components/LectureTable.vue
@@ -0,0 +1,451 @@
+
+
+
+
+ Lectures
+
+
+
+ New Lecture
+
+
+
+
+
+ mdi-pencil
+
+
+ mdi-delete
+
+
+
+ {{ resolveRoom(toRaw(item))?.name ?? "" }}
+
+
+ {{ getScheduleString(toRaw(item)) }}
+
+
+
+
+
+ Edit lecture
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+ Add
+
+
+
+
+
+
+
+ Edit lecture
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+ Save
+
+
+
+
+
+
+ Are you sure you want to delete this item?
+
+
+ Cancel
+ Delete
+
+
+
+
+
+
+
diff --git a/spa-collaboration-vue/src/components/RoomTable.vue b/spa-collaboration-vue/src/components/RoomTable.vue
index 680c1d9..b5083b1 100644
--- a/spa-collaboration-vue/src/components/RoomTable.vue
+++ b/spa-collaboration-vue/src/components/RoomTable.vue
@@ -10,7 +10,7 @@
-
+
mdi-pencil
diff --git a/spa-collaboration-vue/src/components/openEditDialog.ts b/spa-collaboration-vue/src/components/openEditDialog.ts
new file mode 100644
index 0000000..ff83129
--- /dev/null
+++ b/spa-collaboration-vue/src/components/openEditDialog.ts
@@ -0,0 +1,16 @@
+import { N_Lecture } from "metamodel-api-ts/build/dist/L_University_Schedule";
+import { resolveRoom } from "@/modelUtils";
+import { formatModelDateToInputDateString } from "@/dateTimeUtils";
+import { editedNode, editedNodeData, getDate, getEndDate, showEditDialog } from "./LectureTable.vue";
+
+export const openEditDialog = (lecture: N_Lecture) => {
+editedNode.value = lecture;
+editedNodeData.name = lecture.name;
+editedNodeData.description = lecture.description;
+editedNodeData.maximumCapacity = lecture.maximumCapacity;
+editedNodeData.isInRoom = resolveRoom(lecture);
+editedNodeData.date = formatModelDateToInputDateString(getDate(lecture));
+editedNodeData.time = formatModelTimeToInputTimeString(lecture.schedule.get()?.time);
+editedNodeData.endDate = formatModelDateToInputDateString(getEndDate(lecture));
+showEditDialog.value = true;
+};
diff --git a/spa-collaboration-vue/src/dateTimeUtils.ts b/spa-collaboration-vue/src/dateTimeUtils.ts
new file mode 100644
index 0000000..8c039f5
--- /dev/null
+++ b/spa-collaboration-vue/src/dateTimeUtils.ts
@@ -0,0 +1,58 @@
+import { format, formatISO, parse, parseISO } from 'date-fns';
+import { N_Date, N_Time } from 'metamodel-api-ts/build/dist/L_University_Schedule_Time';
+
+// We (miss)use Date for dates and times.
+
+export function parseModelDateString(dateString: string): Date {
+ return parse(dateString, 'dd.MM.yyyy', new Date())
+}
+
+export function formatToModelDateString(date: Date): string {
+ return format(date, 'dd.MM.yyyy',)
+}
+
+export function parseInputDateString(dateString: string): Date {
+ return parseISO(dateString)
+}
+
+export function formatToInputDateString(date: Date): string {
+ return formatISO(date, { representation: 'date' })
+}
+
+export function formatModelDateToInputDateString(date: N_Date | undefined): string | undefined {
+ if (date === undefined || date.date === undefined) {
+ return undefined
+ }
+ return formatToInputDateString(parseModelDateString(date.date))
+}
+
+export function formatInputDateStringToModelDateString(dateString: string): string {
+ return formatToModelDateString(parseInputDateString(dateString))
+}
+
+export function formatToModelTimeString(time: Date): string {
+ return format(time, 'HH:mm')
+}
+
+export function parseModelTimeString(timeString: string): Date {
+ return parse(timeString, 'HH:mm', new Date())
+}
+
+export function formatToInputTimeString(time: Date): string {
+ return format(time, 'HH:mm')
+}
+
+export function parseModelInputTimeString(timeString: string): Date {
+ return parse(timeString, 'HH:mm', new Date())
+}
+
+export function formatModelTimeToInputTimeString(time: N_Time | undefined): string | undefined {
+ if (time === undefined || time.time === undefined) {
+ return undefined
+ }
+ return formatToInputTimeString(parseModelTimeString(time.time))
+}
+
+export function formatInputTimeStringToModelTimeString(timeString: string): string {
+ return formatToModelTimeString(parseModelInputTimeString(timeString))
+}
\ No newline at end of file
diff --git a/spa-collaboration-vue/src/modelUtils.ts b/spa-collaboration-vue/src/modelUtils.ts
index de1cc47..eca2ad6 100644
--- a/spa-collaboration-vue/src/modelUtils.ts
+++ b/spa-collaboration-vue/src/modelUtils.ts
@@ -1,11 +1,14 @@
-import { ITypedNode } from "@modelix/ts-model-api";
+import { LanguageRegistry } from "@modelix/ts-model-api";
+import { ITypedNode, IConceptJS } from "@modelix/ts-model-api";
import { N_BaseConcept } from "metamodel-api-ts/build/dist/L_jetbrains_mps_lang_core";
import { N_Model } from "metamodel-api-ts/build/dist/L_org_modelix_model_repositoryconcepts";
import {
isOfConcept_LectureList,
isOfConcept_RoomList,
isOfConcept_TutorList,
+ N_Lecture,
N_LectureList,
+ N_Room,
N_RoomList,
N_TutorList,
} from "metamodel-api-ts/build/dist/L_University_Schedule";
@@ -39,6 +42,29 @@ export function addNode(node: ITypedNode) {
node.unwrap().getParent()?.removeChild(node.unwrap());
}
-export function findRootNodeById(nodes: N_BaseConcept[], nodeId: string): N_BaseConcept | undefined {
- return nodes.find(node => getNodeId(node) === nodeId)
+export function findRootNodeById(
+ nodes: N_BaseConcept[],
+ nodeId: string,
+): N_BaseConcept | undefined {
+ return nodes.find((node) => getNodeId(node) === nodeId);
+}
+
+export function resolveRoom(lecture: N_Lecture): N_Room | undefined {
+ const reference = lecture.unwrap().getReferenceTargetRef("isInRoom");
+ if (typeof reference === "string" && reference.startsWith("mps")) {
+ // References that look like mps-node:r:ce161c54-ea76-... are invalid and caused by a missing feature.
+ // see. https://issues.modelix.org/issue/MODELIX-234
+ return undefined;
+ }
+ return lecture.isInRoom;
+}
+
+export function setSingleChild(node: ITypedNode, role: string, concept: IConceptJS): T {
+ const untypedNode = node.unwrap()
+ const existingChildren = untypedNode.getChildren(role)
+ for (const existingChild of existingChildren) {
+ untypedNode.removeChild(existingChild)
+ }
+ const child = untypedNode.addNewChild(role, -1, concept)
+ return LanguageRegistry.INSTANCE.wrapNode(child) as T;
}
diff --git a/spa-collaboration-vue/src/views/List.vue b/spa-collaboration-vue/src/views/List.vue
index 91a6d6d..fe614fe 100644
--- a/spa-collaboration-vue/src/views/List.vue
+++ b/spa-collaboration-vue/src/views/List.vue
@@ -6,6 +6,9 @@
+
+
+
@@ -14,9 +17,13 @@