From c92a2e1d70a6e7c9f0d1d74b04bf762247ba2b20 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Wed, 15 Nov 2023 13:56:51 +0800 Subject: [PATCH 01/19] feat: install echarts and initialize graph chart --- app/Dockerfile | 2 +- app/package.json | 1 + app/src/components/QuestMap.vue | 188 ++++++++++++++++++++++++++++++++ app/src/router.js | 7 ++ app/src/views/QuestMapPage.vue | 11 ++ yarn.lock | 20 ++++ 6 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 app/src/components/QuestMap.vue create mode 100644 app/src/views/QuestMapPage.vue diff --git a/app/Dockerfile b/app/Dockerfile index df72388..484da71 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -2,10 +2,10 @@ FROM node:20-alpine as base WORKDIR /app COPY package.json yarn.lock ./ -COPY app/package.json ./app/package.json COPY packages/file-graph/package.json ./packages/file-graph/package.json COPY packages/utils/package.json ./packages/utils/package.json COPY packages/constants/package.json ./packages/constants/package.json +COPY app/package.json ./app/package.json RUN yarn install --frozen-lockfile # Dev stage diff --git a/app/package.json b/app/package.json index c09dc4e..4fbe9d2 100644 --- a/app/package.json +++ b/app/package.json @@ -20,6 +20,7 @@ "@linux-odyssey/file-graph": "*", "@linux-odyssey/utils": "*", "axios": "^1.6.0", + "echarts": "^5.4.3", "socket.io-client": "^4.7.1", "validator": "^13.11.0", "vue": "^3.3.4", diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue new file mode 100644 index 0000000..d344e38 --- /dev/null +++ b/app/src/components/QuestMap.vue @@ -0,0 +1,188 @@ + + + diff --git a/app/src/router.js b/app/src/router.js index 9df8a54..060af3f 100644 --- a/app/src/router.js +++ b/app/src/router.js @@ -3,6 +3,7 @@ import GamePage from './views/GamePage.vue' import LoginPage from './views/LoginPage.vue' import RegisterPage from './views/RegisterPage.vue' import ChooseUsernamePage from './views/ChooseUsernamePage.vue' +import QuestMapPage from './views/QuestMapPage.vue' import { isLoggedIn } from './utils/auth' const routes = [ @@ -40,6 +41,12 @@ const routes = [ component: ChooseUsernamePage, meta: { requiresGuest: true }, }, + { + path: '/map', + name: 'map', + component: QuestMapPage, + meta: { requiresAuth: true }, + }, ] const router = createRouter({ diff --git a/app/src/views/QuestMapPage.vue b/app/src/views/QuestMapPage.vue new file mode 100644 index 0000000..7799b90 --- /dev/null +++ b/app/src/views/QuestMapPage.vue @@ -0,0 +1,11 @@ + + + diff --git a/yarn.lock b/yarn.lock index 851ae5b..660cb2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2253,6 +2253,14 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" +echarts@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.4.3.tgz#f5522ef24419164903eedcfd2b506c6fc91fb20c" + integrity sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA== + dependencies: + tslib "2.3.0" + zrender "5.4.4" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5963,6 +5971,11 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tslib@^2.1.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -6395,3 +6408,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zrender@5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.4.4.tgz#8854f1d95ecc82cf8912f5a11f86657cb8c9e261" + integrity sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw== + dependencies: + tslib "2.3.0" From fae2d8199dbb364e57d28e0e4f1df313f54528e1 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Wed, 15 Nov 2023 14:30:37 +0800 Subject: [PATCH 02/19] feat: calculate layer --- packages/utils/dag.js | 30 +++++++++++++++++++++++ packages/utils/dag.test.js | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 packages/utils/dag.js create mode 100644 packages/utils/dag.test.js diff --git a/packages/utils/dag.js b/packages/utils/dag.js new file mode 100644 index 0000000..22369b4 --- /dev/null +++ b/packages/utils/dag.js @@ -0,0 +1,30 @@ +export default class DAG { + constructor(nodes) { + this.nodes = new Map(nodes.map((node) => [node._id, node])) + this.edges = [] + } + + setLayer(id) { + console.log(`setLayer(${id})`) + const node = this.nodes.get(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + if (node.layer !== undefined) { + return node.layer + } + const previousLayer = node.requirements.map((rId) => this.setLayer(rId)) + node.layer = Math.max(...previousLayer, 0) + 1 + return node.layer + } + + format() { + this.nodes.forEach((_, id) => { + this.setLayer(id) + }) + } + + get(id) { + return this.nodes.get(id) + } +} diff --git a/packages/utils/dag.test.js b/packages/utils/dag.test.js new file mode 100644 index 0000000..272eeea --- /dev/null +++ b/packages/utils/dag.test.js @@ -0,0 +1,50 @@ +import DAG from './dag.js' + +describe('dag', () => { + const data = [ + { + _id: 'helloworld', + title: 'Hello, Linux World!', + requirements: [], + }, + { + _id: 'spell', + title: 'Learn to spell', + requirements: ['helloworld'], + }, + { + _id: 'discover', + title: 'Discover the World!', + requirements: ['spell'], + }, + { + _id: 'read', + title: 'Read the File', + requirements: ['spell'], + }, + { + _id: 'remove', + requirements: ['read', 'discover'], + }, + { + _id: 'move', + requirements: ['helloworld'], + }, + { + _id: 'copy', + requirements: ['remove', 'move'], + }, + ] + + it('should set layers', () => { + const dag = new DAG(data) + dag.format() + expect(dag.get('helloworld').layer).toBe(1) + expect(dag.get('spell').layer).toBe(2) + expect(dag.get('move').layer).toBe(2) + expect(dag.get('read').layer).toBe(3) + expect(dag.get('discover').layer).toBe(3) + expect(dag.get('remove').layer).toBe(4) + expect(dag.get('copy').layer).toBe(5) + }) +}) From e18f0db5ada3c448d94bea6bc1cf587e53f4454a Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Wed, 15 Nov 2023 14:43:34 +0800 Subject: [PATCH 03/19] feat: Add edges --- packages/utils/dag.js | 27 ++++-- packages/utils/dag.test.js | 167 +++++++++++++++++++++++++++---------- 2 files changed, 144 insertions(+), 50 deletions(-) diff --git a/packages/utils/dag.js b/packages/utils/dag.js index 22369b4..d85ce64 100644 --- a/packages/utils/dag.js +++ b/packages/utils/dag.js @@ -1,7 +1,18 @@ export default class DAG { constructor(nodes) { this.nodes = new Map(nodes.map((node) => [node._id, node])) - this.edges = [] + this.edges = new Set() + } + + format() { + this.nodes.forEach((_, id) => { + this.setLayer(id) + this.addEdges(id) + }) + } + + get(id) { + return this.nodes.get(id) } setLayer(id) { @@ -18,13 +29,13 @@ export default class DAG { return node.layer } - format() { - this.nodes.forEach((_, id) => { - this.setLayer(id) + addEdges(id) { + const node = this.nodes.get(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + node.requirements.forEach((rId) => { + this.edges.add([rId, id]) }) } - - get(id) { - return this.nodes.get(id) - } } diff --git a/packages/utils/dag.test.js b/packages/utils/dag.test.js index 272eeea..7dd3398 100644 --- a/packages/utils/dag.test.js +++ b/packages/utils/dag.test.js @@ -1,50 +1,133 @@ import DAG from './dag.js' -describe('dag', () => { - const data = [ - { - _id: 'helloworld', - title: 'Hello, Linux World!', - requirements: [], - }, - { - _id: 'spell', - title: 'Learn to spell', - requirements: ['helloworld'], - }, - { - _id: 'discover', - title: 'Discover the World!', - requirements: ['spell'], - }, - { - _id: 'read', - title: 'Read the File', - requirements: ['spell'], - }, - { - _id: 'remove', - requirements: ['read', 'discover'], - }, - { - _id: 'move', - requirements: ['helloworld'], - }, - { - _id: 'copy', - requirements: ['remove', 'move'], - }, - ] - - it('should set layers', () => { +describe('dag layers', () => { + it('two layer', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + ] const dag = new DAG(data) dag.format() expect(dag.get('helloworld').layer).toBe(1) expect(dag.get('spell').layer).toBe(2) - expect(dag.get('move').layer).toBe(2) - expect(dag.get('read').layer).toBe(3) - expect(dag.get('discover').layer).toBe(3) - expect(dag.get('remove').layer).toBe(4) - expect(dag.get('copy').layer).toBe(5) + }) + it('branch', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + { + _id: 'discover', + requirements: ['helloworld'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('helloworld').layer).toBe(1) + expect(dag.get('spell').layer).toBe(2) + expect(dag.get('discover').layer).toBe(2) + }) + it('merge', () => { + const data = [ + { + _id: 'A', + requirements: [], + }, + { + _id: 'B', + requirements: [], + }, + { + _id: 'C', + requirements: ['A', 'B'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('A').layer).toBe(1) + expect(dag.get('B').layer).toBe(1) + expect(dag.get('C').layer).toBe(2) + }) + it('skip', () => { + const data = [ + { + _id: 'A', + requirements: [], + }, + { + _id: 'B', + requirements: [], + }, + { + _id: 'C', + requirements: ['A'], + }, + { + _id: 'D', + requirements: ['B', 'C'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('A').layer).toBe(1) + expect(dag.get('B').layer).toBe(1) + expect(dag.get('C').layer).toBe(2) + expect(dag.get('D').layer).toBe(3) + }) +}) + +describe('dag edges', () => { + it('one edge', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + ] + + const dag = new DAG(data) + dag.format() + expect(dag.edges).toEqual(new Set([['helloworld', 'spell']])) + }) + + it('branch edges', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + { + _id: 'discover', + requirements: ['helloworld'], + }, + ] + + const dag = new DAG(data) + dag.format() + expect(dag.edges).toEqual( + new Set([ + ['helloworld', 'discover'], + ['helloworld', 'spell'], + ]) + ) }) }) From 14d6ab961bb1aa24ff41429fc5c3ea490158420e Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Wed, 15 Nov 2023 14:46:41 +0800 Subject: [PATCH 04/19] fix: move format into constructor --- packages/utils/dag.js | 3 --- packages/utils/dag.test.js | 6 ------ 2 files changed, 9 deletions(-) diff --git a/packages/utils/dag.js b/packages/utils/dag.js index d85ce64..c3db81b 100644 --- a/packages/utils/dag.js +++ b/packages/utils/dag.js @@ -2,9 +2,6 @@ export default class DAG { constructor(nodes) { this.nodes = new Map(nodes.map((node) => [node._id, node])) this.edges = new Set() - } - - format() { this.nodes.forEach((_, id) => { this.setLayer(id) this.addEdges(id) diff --git a/packages/utils/dag.test.js b/packages/utils/dag.test.js index 7dd3398..2e222c7 100644 --- a/packages/utils/dag.test.js +++ b/packages/utils/dag.test.js @@ -13,7 +13,6 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - dag.format() expect(dag.get('helloworld').layer).toBe(1) expect(dag.get('spell').layer).toBe(2) }) @@ -33,7 +32,6 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - dag.format() expect(dag.get('helloworld').layer).toBe(1) expect(dag.get('spell').layer).toBe(2) expect(dag.get('discover').layer).toBe(2) @@ -54,7 +52,6 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - dag.format() expect(dag.get('A').layer).toBe(1) expect(dag.get('B').layer).toBe(1) expect(dag.get('C').layer).toBe(2) @@ -79,7 +76,6 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - dag.format() expect(dag.get('A').layer).toBe(1) expect(dag.get('B').layer).toBe(1) expect(dag.get('C').layer).toBe(2) @@ -101,7 +97,6 @@ describe('dag edges', () => { ] const dag = new DAG(data) - dag.format() expect(dag.edges).toEqual(new Set([['helloworld', 'spell']])) }) @@ -122,7 +117,6 @@ describe('dag edges', () => { ] const dag = new DAG(data) - dag.format() expect(dag.edges).toEqual( new Set([ ['helloworld', 'discover'], From ca8215486c890b0797b643ca2a759459fff8e229 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Wed, 15 Nov 2023 14:58:00 +0800 Subject: [PATCH 05/19] feat: add dag into frontend (work in progress) --- app/src/components/QuestMap.vue | 161 ++++++++------------------------ packages/utils/dag.js | 15 +++ packages/utils/index.js | 1 + 3 files changed, 56 insertions(+), 121 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index d344e38..b613647 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -18,70 +18,48 @@ import { use, init } from 'echarts/core' import { GraphChart } from 'echarts/charts' import { CanvasRenderer } from 'echarts/renderers' import { TitleComponent, TooltipComponent } from 'echarts/components' +import { DAG } from '@linux-odyssey/utils' const chartContainer = ref(null) let chartInstance = null -const layers = [ - [ - { - name: 'helloworld', - }, - ], - [ - { - name: 'spell', - }, - ], - [ - { - name: 'read', - }, - { - name: 'discover', - }, - ], - [ - { - name: 'remove', - }, - ], -] - -const edges = [ - { - source: 'helloworld', - target: 'spell', - }, - { - source: 'spell', - target: 'read', - }, - { - source: 'spell', - target: 'discover', - }, - { - source: 'read', - target: 'remove', - }, - { - source: 'discover', - target: 'remove', - }, -] - -function toNodes(layers) { - return [ - { - name: 'helloworld', - x: 100, - y: 100, - }, - ] -} use([GraphChart, CanvasRenderer, TitleComponent, TooltipComponent]) +const data = [ + { + _id: 'discover', + title: 'Discover the World!', + requirements: ['spell'], + }, + { + _id: 'read', + title: 'Read the File', + requirements: ['spell'], + }, + { + _id: 'helloworld', + title: 'Hello, Linux World!', + requirements: [], + }, + { + _id: 'spell', + title: 'Learn to spell', + requirements: ['helloworld'], + }, +] + +const dag = new DAG(data) +const nodes = dag.getNodes().map((node) => ({ + name: node._id, + x: 0, + y: 100 * node.layer, +})) + +const edges = dag.getEdges().map((edge) => ({ + source: edge[0], + target: edge[1], +})) + const option = { title: { text: 'Quest Map', @@ -104,69 +82,9 @@ const option = { edgeLabel: { fontSize: 20, }, - data: [ - { - name: 'Node 1', - x: 300, - y: 300, - }, - { - name: 'Node 2', - x: 800, - y: 300, - }, - { - name: 'Node 3', - x: 550, - y: 100, - }, - { - name: 'Node 4', - x: 550, - y: 500, - }, - ], + data: nodes, // links: [], - links: [ - { - source: 0, - target: 1, - symbolSize: [5, 20], - label: { - show: true, - }, - lineStyle: { - width: 5, - curveness: 0.2, - }, - }, - { - source: 'Node 2', - target: 'Node 1', - label: { - show: true, - }, - lineStyle: { - curveness: 0.2, - }, - }, - { - source: 'Node 1', - target: 'Node 3', - }, - { - source: 'Node 2', - target: 'Node 3', - }, - { - source: 'Node 2', - target: 'Node 4', - }, - { - source: 'Node 1', - target: 'Node 4', - }, - ], + links: edges, lineStyle: { opacity: 0.9, width: 2, @@ -175,11 +93,12 @@ const option = { }, ], } - function initChart() { console.log('initChart', chartContainer.value) chartInstance = init(chartContainer.value) chartInstance.setOption(option) + console.log(dag.getNodes()) + console.log(dag.getEdges()) } onMounted(() => { diff --git a/packages/utils/dag.js b/packages/utils/dag.js index c3db81b..79aa95d 100644 --- a/packages/utils/dag.js +++ b/packages/utils/dag.js @@ -2,6 +2,7 @@ export default class DAG { constructor(nodes) { this.nodes = new Map(nodes.map((node) => [node._id, node])) this.edges = new Set() + this.layers = [] this.nodes.forEach((_, id) => { this.setLayer(id) this.addEdges(id) @@ -12,6 +13,14 @@ export default class DAG { return this.nodes.get(id) } + getNodes() { + return Array.from(this.nodes.values()) + } + + getEdges() { + return Array.from(this.edges.values()) + } + setLayer(id) { console.log(`setLayer(${id})`) const node = this.nodes.get(id) @@ -23,6 +32,12 @@ export default class DAG { } const previousLayer = node.requirements.map((rId) => this.setLayer(rId)) node.layer = Math.max(...previousLayer, 0) + 1 + if (this.layers.length < node.layer) { + this.layer.push(1) + } else { + this.layers[node.layer - 1] += 1 + } + node.count = this.layers[node.layer - 1] return node.layer } diff --git a/packages/utils/index.js b/packages/utils/index.js index 8d79493..83029e5 100644 --- a/packages/utils/index.js +++ b/packages/utils/index.js @@ -1,2 +1,3 @@ export { isValidEmail, isValidUsername } from './auth.js' export { buildFileCheckCmd } from './commands.js' +export { default as DAG } from './dag.js' From e018e8dfe4f920e3d9604d956e538f0d019c8e9f Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Thu, 16 Nov 2023 12:46:30 +0800 Subject: [PATCH 06/19] refactor: encapsulate DAG --- .eslintrc.js | 2 +- packages/utils/dag.js | 50 ++++++++++++++++++++++---------------- packages/utils/dag.test.js | 28 ++++++++++----------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 1d3cb3d..7410a96 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -13,7 +13,7 @@ module.exports = { ], parserOptions: { // parser: 'babel-eslint', - ecmaVersion: 2020, + ecmaVersion: 13, sourceType: 'module', }, rules: { diff --git a/packages/utils/dag.js b/packages/utils/dag.js index 79aa95d..de95372 100644 --- a/packages/utils/dag.js +++ b/packages/utils/dag.js @@ -1,53 +1,61 @@ export default class DAG { + #nodes = new Map() + + #edges = new Set() + + #layers = [] + constructor(nodes) { - this.nodes = new Map(nodes.map((node) => [node._id, node])) - this.edges = new Set() - this.layers = [] - this.nodes.forEach((_, id) => { - this.setLayer(id) - this.addEdges(id) + this.#nodes = new Map(nodes.map((node) => [node._id, node])) + this.#edges = new Set() + this.#nodes.forEach((_, id) => { + this.#setLayer(id) + this.#addEdges(id) }) } - get(id) { - return this.nodes.get(id) + getNode(id) { + return this.#nodes.get(id) } getNodes() { - return Array.from(this.nodes.values()) + return Array.from(this.#nodes.values()) } getEdges() { - return Array.from(this.edges.values()) + return this.#edges } - setLayer(id) { - console.log(`setLayer(${id})`) - const node = this.nodes.get(id) + getEdgesArray() { + return Array.from(this.#edges.values()) + } + + #setLayer(id) { + const node = this.getNode(id) if (!node) { throw new Error(`Node ${id} not found`) } if (node.layer !== undefined) { return node.layer } - const previousLayer = node.requirements.map((rId) => this.setLayer(rId)) + const previousLayer = node.requirements.map((rId) => this.#setLayer(rId)) node.layer = Math.max(...previousLayer, 0) + 1 - if (this.layers.length < node.layer) { - this.layer.push(1) + if (this.#layers.length < node.layer) { + this.#layers.push(1) } else { - this.layers[node.layer - 1] += 1 + this.#layers[node.layer - 1] += 1 } - node.count = this.layers[node.layer - 1] + node.index = this.#layers[node.layer - 1] return node.layer } - addEdges(id) { - const node = this.nodes.get(id) + #addEdges(id) { + const node = this.getNode(id) if (!node) { throw new Error(`Node ${id} not found`) } node.requirements.forEach((rId) => { - this.edges.add([rId, id]) + this.#edges.add([rId, id]) }) } } diff --git a/packages/utils/dag.test.js b/packages/utils/dag.test.js index 2e222c7..ba42757 100644 --- a/packages/utils/dag.test.js +++ b/packages/utils/dag.test.js @@ -13,8 +13,8 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - expect(dag.get('helloworld').layer).toBe(1) - expect(dag.get('spell').layer).toBe(2) + expect(dag.getNode('helloworld').layer).toBe(1) + expect(dag.getNode('spell').layer).toBe(2) }) it('branch', () => { const data = [ @@ -32,9 +32,9 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - expect(dag.get('helloworld').layer).toBe(1) - expect(dag.get('spell').layer).toBe(2) - expect(dag.get('discover').layer).toBe(2) + expect(dag.getNode('helloworld').layer).toBe(1) + expect(dag.getNode('spell').layer).toBe(2) + expect(dag.getNode('discover').layer).toBe(2) }) it('merge', () => { const data = [ @@ -52,9 +52,9 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - expect(dag.get('A').layer).toBe(1) - expect(dag.get('B').layer).toBe(1) - expect(dag.get('C').layer).toBe(2) + expect(dag.getNode('A').layer).toBe(1) + expect(dag.getNode('B').layer).toBe(1) + expect(dag.getNode('C').layer).toBe(2) }) it('skip', () => { const data = [ @@ -76,10 +76,10 @@ describe('dag layers', () => { }, ] const dag = new DAG(data) - expect(dag.get('A').layer).toBe(1) - expect(dag.get('B').layer).toBe(1) - expect(dag.get('C').layer).toBe(2) - expect(dag.get('D').layer).toBe(3) + expect(dag.getNode('A').layer).toBe(1) + expect(dag.getNode('B').layer).toBe(1) + expect(dag.getNode('C').layer).toBe(2) + expect(dag.getNode('D').layer).toBe(3) }) }) @@ -97,7 +97,7 @@ describe('dag edges', () => { ] const dag = new DAG(data) - expect(dag.edges).toEqual(new Set([['helloworld', 'spell']])) + expect(dag.getEdges()).toEqual(new Set([['helloworld', 'spell']])) }) it('branch edges', () => { @@ -117,7 +117,7 @@ describe('dag edges', () => { ] const dag = new DAG(data) - expect(dag.edges).toEqual( + expect(dag.getEdges()).toEqual( new Set([ ['helloworld', 'discover'], ['helloworld', 'spell'], From 7a8df716daed02698a0633dade6298f8317a08ca Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Thu, 16 Nov 2023 12:46:53 +0800 Subject: [PATCH 07/19] feat: add x coordinates and links, use SVG --- app/src/components/QuestMap.vue | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index b613647..15e5f11 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -14,16 +14,17 @@ From 08e28f7725e836097d91e1065dd83e6c6ad96979 Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Thu, 16 Nov 2023 13:22:24 +0800 Subject: [PATCH 10/19] feat: load user progress and check before click * not display completed and unlocked status yet --- app/src/components/QuestMap.vue | 42 +++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index 20bfe17..ff87f13 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -1,20 +1,14 @@ From b5be0608aed62e74d757aa83d9efe9831f3fe635 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Sat, 18 Nov 2023 22:10:17 +0800 Subject: [PATCH 11/19] Add background, modify classes --- app/src/components/QuestMap.vue | 53 ++++++++++++++++++++++++++------- app/src/views/QuestMapPage.vue | 8 +++-- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index ff87f13..94d87cb 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -1,7 +1,37 @@ @@ -17,8 +47,8 @@ import { DAG } from '@linux-odyssey/utils' import api from '../utils/api' const marginX = 100 -const marginY = 100 - +const marginY = 70 +const rectImage = '../img/rectImg.png' const chartContainer = ref(null) let chartInstance = null @@ -45,7 +75,7 @@ async function getProgress() { } const genOption = (nodes, edges) => ({ - rendener: 'svg', + render: 'svg', tooltip: {}, animationDurationUpdate: 1500, animationEasingUpdate: 'quinticInOut', @@ -53,22 +83,23 @@ const genOption = (nodes, edges) => ({ { type: 'graph', layout: 'none', - symbolSize: 50, + Symbol: `image://${rectImage}`, + symbolSize: [100, 50], roam: true, label: { show: true, }, edgeSymbol: ['circle', 'arrow'], - edgeSymbolSize: [4, 10], + edgeSymbolSize: [5, 12], edgeLabel: { - fontSize: 20, + fontSize: 25, }, data: nodes, // links: [], links: edges, lineStyle: { - opacity: 0.9, - width: 2, + opacity: 4, + width: 3, curveness: 0, }, }, diff --git a/app/src/views/QuestMapPage.vue b/app/src/views/QuestMapPage.vue index 7799b90..2ad05ab 100644 --- a/app/src/views/QuestMapPage.vue +++ b/app/src/views/QuestMapPage.vue @@ -1,7 +1,11 @@ From 46efb1527329ae81370329c287eda3213b51f749 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Sun, 19 Nov 2023 00:40:16 +0800 Subject: [PATCH 12/19] Change node shape, fix broke (y-axis), add header --- app/src/components/QuestMap.vue | 24 ++++++++++++------------ app/src/img/rect.svg | 3 +++ app/src/img/svg.js | 2 ++ app/src/views/QuestMapPage.vue | 16 ++++++++++------ 4 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 app/src/img/rect.svg create mode 100644 app/src/img/svg.js diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index 94d87cb..1a1bd5d 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -1,6 +1,6 @@ @@ -45,10 +41,10 @@ import { SVGRenderer } from 'echarts/renderers' import { TitleComponent, TooltipComponent } from 'echarts/components' import { DAG } from '@linux-odyssey/utils' import api from '../utils/api' +import { NodeImage } from '../img/svg.js' const marginX = 100 const marginY = 70 -const rectImage = '../img/rectImg.png' const chartContainer = ref(null) let chartInstance = null @@ -83,11 +79,14 @@ const genOption = (nodes, edges) => ({ { type: 'graph', layout: 'none', - Symbol: `image://${rectImage}`, - symbolSize: [100, 50], + symbol: () => { + return NodeImage + }, + symbolSize: [200, 100], roam: true, label: { show: true, + fontSize: 20, }, edgeSymbol: ['circle', 'arrow'], edgeSymbolSize: [5, 12], @@ -102,6 +101,7 @@ const genOption = (nodes, edges) => ({ width: 3, curveness: 0, }, + itemStyle: { color: '#454552' }, }, ], }) diff --git a/app/src/img/rect.svg b/app/src/img/rect.svg new file mode 100644 index 0000000..15481fd --- /dev/null +++ b/app/src/img/rect.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/src/img/svg.js b/app/src/img/svg.js new file mode 100644 index 0000000..13f5e2c --- /dev/null +++ b/app/src/img/svg.js @@ -0,0 +1,2 @@ +export const NodeImage = + 'path://M0.640015 25.0339C0.640015 11.2268 11.8329 0.0339355 25.64 0.0339355H246.36C260.167 0.0339355 271.36 11.2268 271.36 25.0339V113.14C271.36 126.948 260.167 138.14 246.36 138.14H25.64C11.8329 138.14 0.640015 126.948 0.640015 113.14V25.0339Z' diff --git a/app/src/views/QuestMapPage.vue b/app/src/views/QuestMapPage.vue index 2ad05ab..31a07f0 100644 --- a/app/src/views/QuestMapPage.vue +++ b/app/src/views/QuestMapPage.vue @@ -1,15 +1,19 @@ From c272fe61a86c08e5a4c1c4f768e30997e424724f Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 20 Nov 2023 01:38:06 +0800 Subject: [PATCH 13/19] modify background, change color when completed --- app/src/components/QuestMap.vue | 249 ++++++++++++++++++++++++++++---- app/src/views/QuestMapPage.vue | 13 +- 2 files changed, 230 insertions(+), 32 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index 1a1bd5d..75cdc42 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -1,32 +1,218 @@ @@ -44,7 +230,7 @@ import api from '../utils/api' import { NodeImage } from '../img/svg.js' const marginX = 100 -const marginY = 70 +const marginY = 65 const chartContainer = ref(null) let chartInstance = null @@ -63,6 +249,7 @@ async function getQuests() { async function getProgress() { try { const res = await api.get('/users/me') + console.log(res.data.progress) return res.data.progress } catch (err) { console.error(err) @@ -86,10 +273,10 @@ const genOption = (nodes, edges) => ({ roam: true, label: { show: true, - fontSize: 20, + fontSize: 22, }, - edgeSymbol: ['circle', 'arrow'], - edgeSymbolSize: [5, 12], + edgeSymbol: ['pin', 'arrow'], + edgeSymbolSize: [2, 15], edgeLabel: { fontSize: 25, }, @@ -99,9 +286,21 @@ const genOption = (nodes, edges) => ({ lineStyle: { opacity: 4, width: 3, - curveness: 0, + curveness: 0.08, + }, + itemStyle: { + color: (params) => { + const { + data: { completed }, + } = params + switch (completed) { + case true: + return '#ADADB5' + default: + return '#454552' + } + }, }, - itemStyle: { color: '#454552' }, }, ], }) diff --git a/app/src/views/QuestMapPage.vue b/app/src/views/QuestMapPage.vue index 31a07f0..63ea9b6 100644 --- a/app/src/views/QuestMapPage.vue +++ b/app/src/views/QuestMapPage.vue @@ -1,16 +1,15 @@ + + diff --git a/app/src/views/QuestMapPage.vue b/app/src/views/QuestMapPage.vue index 5850f38..56a904a 100644 --- a/app/src/views/QuestMapPage.vue +++ b/app/src/views/QuestMapPage.vue @@ -1,3 +1,8 @@ + + - - From 28373544563fc759e0fd40143a8eba84f3aefba5 Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 20 Nov 2023 15:36:06 +0800 Subject: [PATCH 18/19] render-->renderer --- app/src/components/QuestMap.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index d53504a..9cd8e24 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -38,7 +38,7 @@ async function getProgress() { } const genOption = (nodes, edges) => ({ - render: 'svg', + renderer: 'svg', tooltip: {}, animationDurationUpdate: 1500, animationEasingUpdate: 'quinticInOut', From 83994106a0963db4ceb76f021d536ac7b8ce590c Mon Sep 17 00:00:00 2001 From: Annie Huang Date: Mon, 20 Nov 2023 15:41:08 +0800 Subject: [PATCH 19/19] Add error handling --- app/src/components/QuestMap.vue | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/components/QuestMap.vue b/app/src/components/QuestMap.vue index 9cd8e24..18ed7e6 100644 --- a/app/src/components/QuestMap.vue +++ b/app/src/components/QuestMap.vue @@ -93,10 +93,15 @@ function getOption(quests, progress) { unlocked: node.requirements.every((req) => progress[req]?.completed), })) const lineAppearence = (node) => { - const { unlocked } = node - return { - type: unlocked ? 'line' : 'dashed', - color: unlocked ? '#ADADB5' : '#454552', + try { + const { unlocked } = node + return { + type: unlocked ? 'line' : 'dashed', + color: unlocked ? '#ADADB5' : '#454552', + } + } catch (err) { + console.error(err) + throw err } } const edges = dag.getEdgesArray().map((edge) => ({