add yaml import and export
This commit is contained in:
parent
3c37a94644
commit
dbf19eeb60
@ -2,7 +2,8 @@ import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/clien
|
|||||||
import { onError } from '@apollo/client/link/error';
|
import { onError } from '@apollo/client/link/error';
|
||||||
|
|
||||||
const httpLink = createHttpLink({
|
const httpLink = createHttpLink({
|
||||||
uri: 'http://localhost:4000/graphql',
|
uri: 'http://10.2.2.68:4000/graphql',
|
||||||
|
//uri: 'http://localhost:4000/graphql',
|
||||||
});
|
});
|
||||||
|
|
||||||
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
|
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
|
||||||
|
397
package-lock.json
generated
397
package-lock.json
generated
@ -9,7 +9,13 @@
|
|||||||
"server",
|
"server",
|
||||||
"client",
|
"client",
|
||||||
"shared"
|
"shared"
|
||||||
]
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.0",
|
||||||
|
"@mui/icons-material": "^7.1.1",
|
||||||
|
"@mui/material": "^7.1.1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"client": {
|
"client": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
@ -17,11 +23,12 @@
|
|||||||
"@apollo/client": "^3.9.5",
|
"@apollo/client": "^3.9.5",
|
||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.3",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/material": "^5.15.10",
|
"@mui/material": "^5.17.1",
|
||||||
"@task-receipts/shared": "file:../shared",
|
"@task-receipts/shared": "file:../shared",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"zustand": "^5.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "^18.2.55",
|
||||||
@ -36,6 +43,194 @@
|
|||||||
"vite": "^5.1.0"
|
"vite": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"client/node_modules/@mui/material": {
|
||||||
|
"version": "5.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.17.1.tgz",
|
||||||
|
"integrity": "sha512-2B33kQf+GmPnrvXXweWAx+crbiUEsxCdCN979QDYnlH9ox4pd+0/IBriWLV+l6ORoBF60w39cWjFnJYGFdzXcw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.9",
|
||||||
|
"@mui/core-downloads-tracker": "^5.17.1",
|
||||||
|
"@mui/system": "^5.17.1",
|
||||||
|
"@mui/types": "~7.2.15",
|
||||||
|
"@mui/utils": "^5.17.1",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"@types/react-transition-group": "^4.4.10",
|
||||||
|
"clsx": "^2.1.0",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-is": "^19.0.0",
|
||||||
|
"react-transition-group": "^4.4.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.5.0",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client/node_modules/@mui/material/node_modules/@mui/system": {
|
||||||
|
"version": "5.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.17.1.tgz",
|
||||||
|
"integrity": "sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.9",
|
||||||
|
"@mui/private-theming": "^5.17.1",
|
||||||
|
"@mui/styled-engine": "^5.16.14",
|
||||||
|
"@mui/types": "~7.2.15",
|
||||||
|
"@mui/utils": "^5.17.1",
|
||||||
|
"clsx": "^2.1.0",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.5.0",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client/node_modules/@mui/material/node_modules/@mui/system/node_modules/@mui/private-theming": {
|
||||||
|
"version": "5.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz",
|
||||||
|
"integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.9",
|
||||||
|
"@mui/utils": "^5.17.1",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client/node_modules/@mui/material/node_modules/@mui/system/node_modules/@mui/styled-engine": {
|
||||||
|
"version": "5.16.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz",
|
||||||
|
"integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.9",
|
||||||
|
"@emotion/cache": "^11.13.5",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.4.1",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client/node_modules/@mui/material/node_modules/@mui/types": {
|
||||||
|
"version": "7.2.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
|
||||||
|
"integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client/node_modules/@mui/material/node_modules/@mui/utils": {
|
||||||
|
"version": "5.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz",
|
||||||
|
"integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.9",
|
||||||
|
"@mui/types": "~7.2.15",
|
||||||
|
"@types/prop-types": "^15.7.12",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-is": "^19.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"client/node_modules/@typescript-eslint/eslint-plugin": {
|
"client/node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "7.18.0",
|
"version": "7.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
|
||||||
@ -290,6 +485,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"client/node_modules/react-is": {
|
||||||
|
"version": "19.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||||
|
"integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"client/node_modules/semver": {
|
"client/node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
@ -2491,27 +2692,53 @@
|
|||||||
"url": "https://opencollective.com/mui-org"
|
"url": "https://opencollective.com/mui-org"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/material": {
|
"node_modules/@mui/icons-material": {
|
||||||
"version": "5.17.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.1.tgz",
|
||||||
"integrity": "sha512-2B33kQf+GmPnrvXXweWAx+crbiUEsxCdCN979QDYnlH9ox4pd+0/IBriWLV+l6ORoBF60w39cWjFnJYGFdzXcw==",
|
"integrity": "sha512-X37+Yc8QpEnl0sYmz+WcLFy2dWgNRzbswDzLPXG7QU1XDVlP5TPp1HXjdmCupOWLL/I9m1fyhcyZl8/HPpp/Cg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.27.1"
|
||||||
"@mui/core-downloads-tracker": "^5.17.1",
|
},
|
||||||
"@mui/system": "^5.17.1",
|
"engines": {
|
||||||
"@mui/types": "~7.2.15",
|
"node": ">=14.0.0"
|
||||||
"@mui/utils": "^5.17.1",
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@mui/material": "^7.1.1",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/material": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-mTpdmdZCaHCGOH3SrYM41+XKvNL0iQfM9KlYgpSjgadXx/fEKhhvOktxm8++Xw6FFeOHoOiV+lzOI8X1rsv71A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/core-downloads-tracker": "^7.1.1",
|
||||||
|
"@mui/system": "^7.1.1",
|
||||||
|
"@mui/types": "^7.4.3",
|
||||||
|
"@mui/utils": "^7.1.1",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@types/react-transition-group": "^4.4.10",
|
"@types/react-transition-group": "^4.4.12",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.1",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react-is": "^19.0.0",
|
"react-is": "^19.1.0",
|
||||||
"react-transition-group": "^4.4.5"
|
"react-transition-group": "^4.4.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -2520,6 +2747,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@emotion/react": "^11.5.0",
|
"@emotion/react": "^11.5.0",
|
||||||
"@emotion/styled": "^11.3.0",
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@mui/material-pigment-css": "^7.1.1",
|
||||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
@ -2531,11 +2759,24 @@
|
|||||||
"@emotion/styled": {
|
"@emotion/styled": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@mui/material-pigment-css": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/material/node_modules/@mui/core-downloads-tracker": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-yBckQs4aQ8mqukLnPC6ivIRv6guhaXi8snVl00VtyojBbm+l6VbVhyTSZ68Abcx7Ah8B+GZhrB7BOli+e+9LkQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/material/node_modules/react-is": {
|
"node_modules/@mui/material/node_modules/react-is": {
|
||||||
"version": "19.1.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||||
@ -2543,17 +2784,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@mui/private-theming": {
|
"node_modules/@mui/private-theming": {
|
||||||
"version": "5.17.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.1.tgz",
|
||||||
"integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==",
|
"integrity": "sha512-M8NbLUx+armk2ZuaxBkkMk11ultnWmrPlN0Xe3jUEaBChg/mcxa5HWIWS1EE4DF36WRACaAHVAvyekWlDQf0PQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@mui/utils": "^5.17.1",
|
"@mui/utils": "^7.1.1",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -2570,18 +2811,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/styled-engine": {
|
"node_modules/@mui/styled-engine": {
|
||||||
"version": "5.16.14",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.1.tgz",
|
||||||
"integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==",
|
"integrity": "sha512-R2wpzmSN127j26HrCPYVQ53vvMcT5DaKLoWkrfwUYq3cYytL6TQrCH8JBH3z79B6g4nMZZVoaXrxO757AlShaw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@emotion/cache": "^11.13.5",
|
"@emotion/cache": "^11.13.5",
|
||||||
|
"@emotion/serialize": "^1.3.3",
|
||||||
|
"@emotion/sheet": "^1.4.0",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -2602,22 +2845,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/system": {
|
"node_modules/@mui/system": {
|
||||||
"version": "5.17.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.1.tgz",
|
||||||
"integrity": "sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg==",
|
"integrity": "sha512-Kj1uhiqnj4Zo7PDjAOghtXJtNABunWvhcRU0O7RQJ7WOxeynoH6wXPcilphV8QTFtkKaip8EiNJRiCD+B3eROA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@mui/private-theming": "^5.17.1",
|
"@mui/private-theming": "^7.1.1",
|
||||||
"@mui/styled-engine": "^5.16.14",
|
"@mui/styled-engine": "^7.1.1",
|
||||||
"@mui/types": "~7.2.15",
|
"@mui/types": "^7.4.3",
|
||||||
"@mui/utils": "^5.17.1",
|
"@mui/utils": "^7.1.1",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.1",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -2642,10 +2885,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/types": {
|
"node_modules/@mui/types": {
|
||||||
"version": "7.2.24",
|
"version": "7.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz",
|
||||||
"integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
|
"integrity": "sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
},
|
},
|
||||||
@ -2656,20 +2902,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/utils": {
|
"node_modules/@mui/utils": {
|
||||||
"version": "5.17.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.1.tgz",
|
||||||
"integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==",
|
"integrity": "sha512-BkOt2q7MBYl7pweY2JWwfrlahhp+uGLR8S+EhiyRaofeRYUWL2YKbSGQvN4hgSN1i8poN0PaUiii1kEMrchvzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@mui/types": "~7.2.15",
|
"@mui/types": "^7.4.3",
|
||||||
"@types/prop-types": "^15.7.12",
|
"@types/prop-types": "^15.7.14",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react-is": "^19.0.0"
|
"react-is": "^19.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -3411,6 +3657,12 @@
|
|||||||
"pretty-format": "^29.0.0"
|
"pretty-format": "^29.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/js-yaml": {
|
||||||
|
"version": "4.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
|
||||||
|
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/long": {
|
"node_modules/@types/long": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||||
@ -11795,6 +12047,35 @@
|
|||||||
"zen-observable": "0.8.15"
|
"zen-observable": "0.8.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=18.0.0",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"use-sync-external-store": ">=1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"name": "task-receipts-server",
|
"name": "task-receipts-server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -11803,11 +12084,13 @@
|
|||||||
"@node-escpos/core": "^0.6.0",
|
"@node-escpos/core": "^0.6.0",
|
||||||
"@node-escpos/usb-adapter": "^0.3.1",
|
"@node-escpos/usb-adapter": "^0.3.1",
|
||||||
"@task-receipts/shared": "file:../shared",
|
"@task-receipts/shared": "file:../shared",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
"apollo-server-express": "^3.13.0",
|
"apollo-server-express": "^3.13.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
@ -12021,6 +12304,12 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"server/node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
|
"license": "Python-2.0"
|
||||||
|
},
|
||||||
"server/node_modules/brace-expansion": {
|
"server/node_modules/brace-expansion": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
@ -12059,6 +12348,18 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"server/node_modules/js-yaml": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"server/node_modules/minimatch": {
|
"server/node_modules/minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
@ -12,5 +12,11 @@
|
|||||||
"test": "npm run test --workspaces",
|
"test": "npm run test --workspaces",
|
||||||
"lint": "npm run lint --workspaces",
|
"lint": "npm run lint --workspaces",
|
||||||
"lint:fix": "npm run lint:fix --workspaces"
|
"lint:fix": "npm run lint:fix --workspaces"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.0",
|
||||||
|
"@mui/icons-material": "^7.1.1",
|
||||||
|
"@mui/material": "^7.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,13 @@
|
|||||||
"@node-escpos/core": "^0.6.0",
|
"@node-escpos/core": "^0.6.0",
|
||||||
"@node-escpos/usb-adapter": "^0.3.1",
|
"@node-escpos/usb-adapter": "^0.3.1",
|
||||||
"@task-receipts/shared": "file:../shared",
|
"@task-receipts/shared": "file:../shared",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
"apollo-server-express": "^3.13.0",
|
"apollo-server-express": "^3.13.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
|
75
server/src/__tests__/yaml-service.test.ts
Normal file
75
server/src/__tests__/yaml-service.test.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { YamlService } from '../yaml-service';
|
||||||
|
import { UserRepository } from '../db/repositories/user-repository';
|
||||||
|
import knex from 'knex';
|
||||||
|
import config from '../db/knexfile';
|
||||||
|
|
||||||
|
const MIGRATIONS_DIR = config.test.migrations?.directory;
|
||||||
|
|
||||||
|
// Helper to truncate all tables
|
||||||
|
async function truncateAll(db: any) {
|
||||||
|
await db.raw('PRAGMA foreign_keys = OFF');
|
||||||
|
await db('notes').truncate();
|
||||||
|
await db('steps').truncate();
|
||||||
|
await db('tasks').truncate();
|
||||||
|
await db('groups').truncate();
|
||||||
|
await db('users').truncate();
|
||||||
|
await db('images').truncate();
|
||||||
|
await db('print_history').truncate();
|
||||||
|
await db.raw('PRAGMA foreign_keys = ON');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('YamlService', () => {
|
||||||
|
let db: ReturnType<typeof knex>;
|
||||||
|
let yamlService: YamlService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
db = knex(config.test);
|
||||||
|
await db.migrate.latest({ directory: MIGRATIONS_DIR });
|
||||||
|
// Create a user for notes
|
||||||
|
const userRepo = new UserRepository(db);
|
||||||
|
await userRepo.create({ name: 'Test User' });
|
||||||
|
yamlService = new YamlService(db);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export empty database to YAML', async () => {
|
||||||
|
const yamlContent = await yamlService.exportToYaml();
|
||||||
|
expect(yamlContent).toContain('groups: []');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should import and export YAML correctly', async () => {
|
||||||
|
const testYaml = `
|
||||||
|
groups:
|
||||||
|
- name: Test Group
|
||||||
|
tasks:
|
||||||
|
- name: Test Task
|
||||||
|
steps:
|
||||||
|
- name: Test Step
|
||||||
|
instructions: Test instructions
|
||||||
|
notes:
|
||||||
|
- content: Test note
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Import the YAML
|
||||||
|
await yamlService.importFromYaml(testYaml);
|
||||||
|
|
||||||
|
// Export it back
|
||||||
|
const exportedYaml = await yamlService.exportToYaml();
|
||||||
|
|
||||||
|
// Should contain the imported data
|
||||||
|
expect(exportedYaml).toContain('Test Group');
|
||||||
|
expect(exportedYaml).toContain('Test Task');
|
||||||
|
expect(exportedYaml).toContain('Test Step');
|
||||||
|
expect(exportedYaml).toContain('Test instructions');
|
||||||
|
expect(exportedYaml).toContain('Test note');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle invalid YAML', async () => {
|
||||||
|
const invalidYaml = 'invalid: yaml: content:';
|
||||||
|
|
||||||
|
await expect(yamlService.importFromYaml(invalidYaml)).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
@ -8,16 +8,18 @@ import { createDb } from './db';
|
|||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import { createPrinter } from './printer';
|
import { createPrinter } from './printer';
|
||||||
import { PrintHistoryRepository, StepRepository } from './db/repositories';
|
import { PrintHistoryRepository, StepRepository } from './db/repositories';
|
||||||
|
import { YamlService } from './yaml-service';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = process.env.PORT || 4000;
|
const port = 4000; //process.env.PORT || 4000;
|
||||||
|
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
const db = createDb();
|
const db = createDb();
|
||||||
const printHistoryRepo = new PrintHistoryRepository(db);
|
const printHistoryRepo = new PrintHistoryRepository(db);
|
||||||
const stepRepo = new StepRepository(db);
|
const stepRepo = new StepRepository(db);
|
||||||
const printer = createPrinter(printHistoryRepo, stepRepo);
|
const printer = createPrinter(printHistoryRepo, stepRepo);
|
||||||
|
const yamlService = new YamlService(db);
|
||||||
|
|
||||||
const server = new ApolloServer({
|
const server = new ApolloServer({
|
||||||
typeDefs,
|
typeDefs,
|
||||||
@ -28,7 +30,9 @@ async function startServer() {
|
|||||||
|
|
||||||
// Enable CORS for the frontend
|
// Enable CORS for the frontend
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: 'http://localhost:5173',
|
// origin: 'http://localhost:5173',
|
||||||
|
// allow all origins
|
||||||
|
origin: '*',
|
||||||
credentials: true
|
credentials: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -40,8 +44,40 @@ async function startServer() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const httpServer = app.listen(port, () => {
|
// YAML Export endpoint
|
||||||
|
app.get('/api/yaml/export', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const yamlContent = await yamlService.exportToYaml();
|
||||||
|
res.setHeader('Content-Type', 'text/yaml');
|
||||||
|
res.setHeader('Content-Disposition', 'attachment; filename="database-export.yaml"');
|
||||||
|
res.send(yamlContent);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error exporting YAML:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to export YAML' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// YAML Import endpoint
|
||||||
|
app.post('/api/yaml/import', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { yamlContent } = req.body;
|
||||||
|
|
||||||
|
if (!yamlContent || typeof yamlContent !== 'string') {
|
||||||
|
return res.status(400).json({ error: 'YAML content is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await yamlService.importFromYaml(yamlContent);
|
||||||
|
res.json({ message: 'Database imported successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error importing YAML:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to import YAML' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const httpServer = app.listen(port, '0.0.0.0', () => {
|
||||||
logger.info(`Server running at http://localhost:${port}/graphql`);
|
logger.info(`Server running at http://localhost:${port}/graphql`);
|
||||||
|
logger.info(`YAML export endpoint: http://localhost:${port}/api/yaml/export`);
|
||||||
|
logger.info(`YAML import endpoint: http://localhost:${port}/api/yaml/import`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { PAPER_CONFIG } from "./printer-constants";
|
||||||
|
|
||||||
export const formatUtils = {
|
export const formatUtils = {
|
||||||
/**
|
/**
|
||||||
* Creates a banner line with the specified character
|
* Creates a banner line with the specified character
|
||||||
@ -5,7 +7,7 @@ export const formatUtils = {
|
|||||||
* @param length Length of the banner
|
* @param length Length of the banner
|
||||||
* @returns Formatted banner string
|
* @returns Formatted banner string
|
||||||
*/
|
*/
|
||||||
createBanner(char: string, length: number = 40): string {
|
createBanner(char: string, length: number = PAPER_CONFIG.BANNER_LENGTH): string {
|
||||||
return char.repeat(length);
|
return char.repeat(length);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ export class SerialPrinter implements PrinterInterface {
|
|||||||
const step = taskSteps[i];
|
const step = taskSteps[i];
|
||||||
const stepSection = formatUtils.formatSection(
|
const stepSection = formatUtils.formatSection(
|
||||||
formatUtils.formatStepHeader(step.name, i + 1, task.name, true),
|
formatUtils.formatStepHeader(step.name, i + 1, task.name, true),
|
||||||
step.instructions,
|
step.instructions || 'No instructions provided',
|
||||||
'-'
|
'-'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ export class SerialPrinter implements PrinterInterface {
|
|||||||
|
|
||||||
const stepSection = formatUtils.formatSection(
|
const stepSection = formatUtils.formatSection(
|
||||||
formatUtils.formatStepHeader(step.name, stepNumber, task?.name),
|
formatUtils.formatStepHeader(step.name, stepNumber, task?.name),
|
||||||
step.instructions
|
step.instructions || 'No instructions provided'
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.printer
|
await this.printer
|
||||||
|
187
server/src/yaml-service.ts
Normal file
187
server/src/yaml-service.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import * as yaml from 'js-yaml';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { GroupRepository, TaskRepository, StepRepository, NoteRepository } from './db/repositories';
|
||||||
|
import { Group, Task, Step, Note } from '@shared/index';
|
||||||
|
|
||||||
|
export interface YamlData {
|
||||||
|
groups: YamlGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YamlGroup {
|
||||||
|
name: string;
|
||||||
|
tasks: YamlTask[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YamlTask {
|
||||||
|
name: string;
|
||||||
|
steps: YamlStep[];
|
||||||
|
notes?: YamlNote[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YamlStep {
|
||||||
|
name: string;
|
||||||
|
instructions: string;
|
||||||
|
notes?: YamlNote[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YamlNote {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class YamlService {
|
||||||
|
private groupRepo: GroupRepository;
|
||||||
|
private taskRepo: TaskRepository;
|
||||||
|
private stepRepo: StepRepository;
|
||||||
|
private noteRepo: NoteRepository;
|
||||||
|
private db: Knex;
|
||||||
|
|
||||||
|
constructor(db: Knex) {
|
||||||
|
this.db = db;
|
||||||
|
this.groupRepo = new GroupRepository(db);
|
||||||
|
this.taskRepo = new TaskRepository(db);
|
||||||
|
this.stepRepo = new StepRepository(db);
|
||||||
|
this.noteRepo = new NoteRepository(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
async exportToYaml(): Promise<string> {
|
||||||
|
const groups = await this.groupRepo.findRootGroups();
|
||||||
|
const yamlData: YamlData = {
|
||||||
|
groups: []
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
const yamlGroup = await this.convertGroupToYaml(group);
|
||||||
|
yamlData.groups.push(yamlGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaml.dump(yamlData, {
|
||||||
|
indent: 2,
|
||||||
|
lineWidth: 120,
|
||||||
|
noRefs: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async importFromYaml(yamlContent: string): Promise<void> {
|
||||||
|
const yamlData = yaml.load(yamlContent) as YamlData;
|
||||||
|
|
||||||
|
if (!yamlData || !yamlData.groups) {
|
||||||
|
throw new Error('Invalid YAML format: missing groups');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear existing data (optional - you might want to make this configurable)
|
||||||
|
await this.clearExistingData();
|
||||||
|
|
||||||
|
// Import groups and their nested data
|
||||||
|
for (const yamlGroup of yamlData.groups) {
|
||||||
|
await this.importGroup(yamlGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async convertGroupToYaml(group: Group): Promise<YamlGroup> {
|
||||||
|
const tasks = await this.taskRepo.findByGroupId(group.id);
|
||||||
|
const yamlTasks: YamlTask[] = [];
|
||||||
|
|
||||||
|
for (const task of tasks) {
|
||||||
|
const yamlTask = await this.convertTaskToYaml(task);
|
||||||
|
yamlTasks.push(yamlTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: group.name,
|
||||||
|
tasks: yamlTasks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async convertTaskToYaml(task: Task): Promise<YamlTask> {
|
||||||
|
const steps = await this.stepRepo.findByTaskId(task.id);
|
||||||
|
const notes = await this.noteRepo.findByTaskId(task.id);
|
||||||
|
|
||||||
|
const yamlSteps: YamlStep[] = [];
|
||||||
|
for (const step of steps) {
|
||||||
|
const stepNotes = await this.noteRepo.findByStepId(step.id);
|
||||||
|
const yamlStep: YamlStep = {
|
||||||
|
name: step.name,
|
||||||
|
instructions: step.instructions,
|
||||||
|
notes: stepNotes.map(note => ({ content: note.content }))
|
||||||
|
};
|
||||||
|
yamlSteps.push(yamlStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
const yamlTask: YamlTask = {
|
||||||
|
name: task.name,
|
||||||
|
steps: yamlSteps,
|
||||||
|
notes: notes.map(note => ({ content: note.content }))
|
||||||
|
};
|
||||||
|
|
||||||
|
return yamlTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async importGroup(yamlGroup: YamlGroup): Promise<void> {
|
||||||
|
// Create the group
|
||||||
|
const group = await this.groupRepo.create({
|
||||||
|
name: yamlGroup.name,
|
||||||
|
parent_id: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
// Import tasks for this group
|
||||||
|
for (const yamlTask of yamlGroup.tasks) {
|
||||||
|
await this.importTask(yamlTask, group.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async importTask(yamlTask: YamlTask, groupId: number): Promise<void> {
|
||||||
|
// Create the task
|
||||||
|
const task = await this.taskRepo.create({
|
||||||
|
name: yamlTask.name,
|
||||||
|
group_id: groupId,
|
||||||
|
print_count: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Import task-level notes
|
||||||
|
if (yamlTask.notes) {
|
||||||
|
for (const yamlNote of yamlTask.notes) {
|
||||||
|
await this.noteRepo.create({
|
||||||
|
content: yamlNote.content,
|
||||||
|
task_id: task.id,
|
||||||
|
created_by: 1 // Default user ID
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import steps
|
||||||
|
for (let i = 0; i < yamlTask.steps.length; i++) {
|
||||||
|
const yamlStep = yamlTask.steps[i];
|
||||||
|
await this.importStep(yamlStep, task.id, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async importStep(yamlStep: YamlStep, taskId: number, order: number): Promise<void> {
|
||||||
|
// Create the step
|
||||||
|
const step = await this.stepRepo.create({
|
||||||
|
name: yamlStep.name,
|
||||||
|
instructions: yamlStep.instructions,
|
||||||
|
task_id: taskId,
|
||||||
|
order: order,
|
||||||
|
print_count: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Import step-level notes
|
||||||
|
if (yamlStep.notes) {
|
||||||
|
for (const yamlNote of yamlStep.notes) {
|
||||||
|
await this.noteRepo.create({
|
||||||
|
content: yamlNote.content,
|
||||||
|
step_id: step.id,
|
||||||
|
created_by: 1 // Default user ID
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async clearExistingData(): Promise<void> {
|
||||||
|
// Delete in reverse order to respect foreign key constraints
|
||||||
|
await this.db('notes').del();
|
||||||
|
await this.db('steps').del();
|
||||||
|
await this.db('tasks').del();
|
||||||
|
await this.db('groups').del();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user