Loading zip file OK, Still need to clenup and refactor loading and compilation

This commit is contained in:
Jérémy Munsch 2024-01-09 16:24:38 +01:00
parent 77da5b21ed
commit 1f8e27111a
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
21 changed files with 703 additions and 65 deletions

7
.gitignore vendored
View file

@ -1,5 +1,6 @@
# Dist electron
dist-electron
release
# Nuxt dev/build outputs
.output
@ -25,3 +26,9 @@ logs
.env
.env.*
!.env.example
# public assets
public/assets
# zip files
static/data/*.zip

2
.npmrc
View file

@ -1,2 +1,4 @@
# For electron-builder
# https://github.com/electron-userland/electron-builder/issues/6289#issuecomment-1042620422
shamefully-hoist=true
strict-peer-dependencies=false

View file

@ -20,10 +20,12 @@
</template>
<script lang="ts">
import Modal from "bootstrap/js/dist/modal";
import KonamiCode from "konami-code-js";
import Swal from "sweetalert2";
import { defineComponent } from "vue";
import dataJson from "public/assets/data.json";
import data from "data/test.json";
export default defineComponent({
@ -34,6 +36,7 @@ export default defineComponent({
modal: Modal;
adminModal: Modal;
data: typeof data;
dataJson: TypeApp;
selectedItem: Item | null;
konami: KonamiCode;
contacts: Array<Contact>;
@ -42,6 +45,7 @@ export default defineComponent({
modalId: "productModal",
adminModalId: "adminModal",
data,
dataJson,
selectedItem: null,
modal: {} as Modal,
adminModal: {} as Modal,

44
composable/useElectron.ts Normal file
View file

@ -0,0 +1,44 @@
import log from "electron-log";
export default function useElectron() {
const isServer = process.server || typeof window === "undefined" || typeof window.require === "undefined";
const isElectron = !isServer && navigator.userAgent.toLowerCase().includes("nuxt-app");
log.info(`isServer ${isServer}`);
log.info(`isElectron ${isElectron}`);
log.info(`userAgent ${navigator.userAgent.toLowerCase()}`);
if (!isElectron || isServer) return { isElectron };
// Initialize electron
const electron = window.require("electron");
// Window title bar actions
// ========================
const titleBarActions = {
minimize: () => electron.ipcRenderer.invoke("titlebar:action", "minimize"),
toggleMaximize: () => electron.ipcRenderer.invoke("titlebar:action", "toggleMaximize"),
isMaximized: () => electron.ipcRenderer.invoke("isMaximized:app", null),
close: () => electron.ipcRenderer.invoke("close:app", null)
};
// Window title bar stats
// ======================
const windowStats = ref({
isMaximized: false,
isFullscreen: false
});
electron.ipcRenderer.on("window:maximizeChanged", (_event, value) => {
windowStats.value.isMaximized = value;
});
electron.ipcRenderer.on("window:fullscreenChanged", (_event, value) => {
windowStats.value.isFullscreen = value;
});
// Get Json Data
const sendSync = (): TypeApp => {
return electron.ipcRenderer.sendSync("get-config", "");
};
// Initialize ipcRenderer
return { isElectron, titleBarActions, windowStats, sendSync };
}

30
electron-builder.json5 Normal file
View file

@ -0,0 +1,30 @@
/**
* @see https://www.electron.build/configuration/configuration
*/
{
appId: "your.app.id",
asar: false,
directories: {
output: "release/${version}",
},
files: [".output/**/*", "dist-electron"],
mac: {
artifactName: "${productName}_${version}.${ext}",
target: ["dmg"],
},
win: {
target: [
{
target: "nsis",
arch: ["x64"],
},
],
artifactName: "${productName}_${version}.${ext}",
},
nsis: {
oneClick: false,
perMachine: false,
allowToChangeInstallationDirectory: true,
deleteAppDataOnUninstall: false,
},
}

94
electron/UnzipData.ts Normal file
View file

@ -0,0 +1,94 @@
import fs from "node:fs";
import AdmZip from "adm-zip";
import Ajv from "ajv";
import addFormats from "ajv-formats";
import { app } from "electron";
import log from "electron-log/main";
/**
* In charge to unzip data from showroom Bo,
* validate data config and check version
*/
export default class UnzipData {
static zipFileToUse: string|null;
/**
* Init zipFileToUse if not already inited
*/
private static init() {
if (!UnzipData.zipFileToUse) {
// * Try to locate last zip file and load it.
const zipFile = fs.readdirSync("static/data")
.map(path => { return {path: `static/data/${path}`, "stat": fs.lstatSync(`static/data/${path}`)}; })
.filter(obj => obj.stat.isFile() && obj.path.endsWith(".zip"))
.sort((objA, objB) => objB.stat.mtime.getTime() - objA.stat.mtime.getTime())
.at(0) ?? null;
if (zipFile) {
UnzipData.zipFileToUse = zipFile.path;
}
}
}
/**
* Decompress zip file content
* And fetch data.json content
*/
public static loadZipFileContent(): TypeApp|null {
UnzipData.init();
if (!UnzipData.zipFileToUse) {
return null;
}
const zip = new AdmZip(fs.readFileSync(UnzipData.zipFileToUse));
// * Clear export dir
fs.rmSync("public/assets", { recursive: true, force: true });
zip.extractAllTo("public/assets");
const jsonData = fs.readFileSync("public/assets/data.json", { encoding: "utf8", flag: "r" });
const jsonSchemaPath = "schemas";
if (!UnzipData.validateAgainstJsonSchema(jsonSchemaPath, jsonData)) {
log.warn(`Data for file ${UnzipData.zipFileToUse} is not valid JSON`);
return null;
}
const appJsonConfig: TypeApp = JSON.parse(jsonData);
const appVersion: string = app.getVersion();
if (appJsonConfig.version !== appVersion) {
throw new Error(`App version ${appVersion} is not compatible with config version ${appJsonConfig.version}`);
}
return appJsonConfig;
}
private static validateAgainstJsonSchema(jsonSchemaPath:string, jsonData: string): boolean {
try {
const schemaList = fs.readdirSync(jsonSchemaPath)
.filter(schemaPath => schemaPath.endsWith(".schema.json"))
.map(schemaPath => JSON.parse(fs.readFileSync(`schemas/${schemaPath}`, { encoding: "utf8", flag: "r" })) as Record<string,string|any>);
const appSchemaIndex = schemaList.findIndex((schema) => String(schema.$id).endsWith("app.schema.json"));
if (appSchemaIndex === -1) {
throw new Error("app.schema.json not found");
}
const appSchema = schemaList.splice(appSchemaIndex, 1).at(0);
if (!appSchema) {
throw new Error("app.schema.json not found");
}
const ajv = new Ajv();
addFormats(ajv);
ajv.addMetaSchema(appSchema);
ajv.addSchema(schemaList);
return ajv.validate(appSchema, JSON.parse(jsonData));
} catch (err:unknown) {
if (err instanceof Error) {
log.error(err);
return false;
}
throw new Error("validateAgainstJsonSchema unkown error");
}
}
}

View file

@ -1,10 +1,12 @@
/* eslint-disable */
import { BrowserWindow, app } from 'electron'
import { BrowserWindow, app, type BrowserWindowConstructorOptions } from 'electron'
import { EventEmitter } from 'events'
const DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL
const isProduction = process.env.NODE_ENV === 'production'
const isDev = process.env.NODE_ENV === 'development'
const fs = import("node:fs");
export default class BrowserWinHandler {
private _eventEmitter: EventEmitter;
@ -13,10 +15,11 @@ export default class BrowserWinHandler {
private browserWindow: BrowserWindow|null;
/**
* @param [options] {object} - browser window options
* @param [allowRecreate] {boolean}
*/
constructor (options: object, allowRecreate: boolean = true) {
*
* @param options browser window options
* @param allowRecreate
*/
constructor (options: BrowserWindowConstructorOptions, allowRecreate: boolean = true) {
this._eventEmitter = new EventEmitter()
this.allowRecreate = allowRecreate
this.options = options
@ -42,22 +45,27 @@ export default class BrowserWinHandler {
}
_create () {
this.browserWindow = new BrowserWindow(
{
this.browserWindow = new BrowserWindow({
...this.options,
webPreferences: {
...this.options.webPreferences,
webSecurity: isProduction, // disable on dev to allow loading local resources
nodeIntegration: true, // allow loading modules via the require () function
contextIsolation: false, // https://github.com/electron/electron/issues/18037#issuecomment-806320028
...this.options.webPreferences,
webSecurity: isProduction, // disable on dev to allow loading local resources
nodeIntegration: false,
contextIsolation: true
}
}
)
this.browserWindow.on('closed', () => {
// Dereference the window object
this.browserWindow = null
})
this._eventEmitter.emit('created')
this.browserWindow.on('closed', () => {
// Dereference the window object
this.browserWindow = null
})
this._eventEmitter.emit('created');
this.browserWindow.webContents.on('before-input-event', (_, input) => {
if (input.type === 'keyDown' && input.key === 'F12' && this.browserWindow) {
this.browserWindow.webContents.openDevTools({ mode: 'detach' });
}
});
this.browserWindow.removeMenu();
this.browserWindow.setMenu(null);
}
_recreate () {
@ -87,4 +95,26 @@ export default class BrowserWinHandler {
this.onCreated(() => resolve(this.browserWindow))
})
}
/**
* Open dev tools if ti can be
*/
openDevTool() {
const browserWindow = this.browserWindow;
if (!browserWindow) {
throw new Error("No window is loaded");
}
browserWindow.webContents.openDevTools();
}
quit() {
const browserWindow = this.browserWindow;
if (!browserWindow) {
throw new Error("No window is loaded");
}
browserWindow.on('closed', () => {
this.browserWindow = null
});
browserWindow.close();
}
}

View file

@ -1,7 +1,13 @@
import { app } from "electron";
import path from "node:path";
import { app, ipcMain } from "electron";
import log from "electron-log/main";
import UnzipData from "./UnzipData";
import BrowserWinHandler from "./browserWinHandler";
// TODO: take inspiration form https://github.com/michalzaq12/electron-nuxt/tree/master/template/src/main.
// Or https://github.com/EternalC0der/electron-nuxt3
log.initialize();
// Quit when all windows are closed.
app.on("window-all-closed", function () {
@ -14,15 +20,36 @@ app.whenReady().then(() => {
// * Run an Electron window ?
if (process.env.npm_lifecycle_event === "electron") {
const winHandler = new BrowserWinHandler({
kiosk: true,
closable: false,
alwaysOnTop: true,
fullScreen: true,
autoHideMenuBar: true
kiosk: false,
closable: true,
alwaysOnTop: false,
fullscreen: false,
autoHideMenuBar: false,
webPreferences: {
preload: path.join(__dirname, "versions.js")
}
});
winHandler.onCreated(_browserWindow => {
winHandler.loadPage("ranges");
try {
const appData = UnzipData.loadZipFileContent();
if (!appData) {
throw new Error("No zip file to load");
}
// this is the event listener that will respond when we will request it in the web page
ipcMain.on("get-config", (event) => {
log.info("read json app config");
event.returnValue = appData;
});
winHandler.loadPage("ranges");
winHandler.openDevTool();
} catch (err) {
log.error(err);
winHandler.quit();
}
});
// fs.readFile();
}
});

View file

@ -0,0 +1,9 @@
import { contextBridge } from "electron";
// This runs in sandboxed mode.
contextBridge.exposeInMainWorld("versions", {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
// Nous pouvons exposer des variables en plus des fonctions
});

4
electron/renderer.ts Normal file
View file

@ -0,0 +1,4 @@
// console.log("versions.chrome()");
// console.log(app.chrome());

12
index.d.ts vendored
View file

@ -22,6 +22,8 @@ type SettingsModel = {
color: string;
/** Éléments par ligne, entre 3 et 6 inclus */
items_by_line: number;
created_at: string;
updated_at: string;
};
/** A brand from Showroom Bo */
@ -71,7 +73,7 @@ type ProductModel = {
/** Logo du produit, chemin tel que présent dans le zip */
picture: string;
/** Clé étrangère de la gamme */
range_id: string;
range_id: number;
created_at: string;
updated_at: string;
};
@ -116,6 +118,14 @@ declare global {
type Settings = ModalSettings;
type Contact = ContactModel;
// * App Models
type TypeApp = AppModel;
type TypeSettings = SettingsModel;
type TypeBrand = BrandModel;
type TypeRange = RangeModel;
type TypeProduct = ProductModel;
// Nuxt
type RouteLocation = NRouteLocation;

View file

@ -10,9 +10,18 @@ export default defineNuxtConfig({
build: [
{
// Main-Process entry file of the Electron App.
entry: "electron/main.ts",
entry: "electron/main.ts"
},
{
entry: "electron/preload/versions.ts",
onstart(args) {
// Notify the Renderer-Process to reload the page when the Preload-Scripts build is complete,
// instead of restarting the entire Electron App.
args.reload();
},
},
],
renderer: {},
},
devtools: { enabled: false },
app: {

189
package-lock.json generated
View file

@ -1,10 +1,12 @@
{
"name": "nuxt-app",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nuxt-app",
"version": "1.0.0",
"hasInstallScript": true,
"devDependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.1",
@ -14,14 +16,19 @@
"@nuxt/devtools": "^1.0.0-beta.1",
"@nuxtjs/eslint-config-typescript": "latest",
"@popperjs/core": "^2.11.8",
"@types/adm-zip": "^0.5.5",
"@types/bootstrap": "^5.2.10",
"@types/dompurify": "^3.0.3",
"@types/konami-code-js": "^0.8.1",
"@vue/eslint-config-typescript": "^12.0.0",
"adm-zip": "^0.5.10",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"bootstrap": "^5.3.2",
"dompurify": "^3.0.6",
"electron": "^28.1.1",
"electron-builder": "^24.9.1",
"electron-log": "^5.0.3",
"eslint": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-nuxt": "latest",
@ -1040,6 +1047,37 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/@develar/schema-utils/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@develar/schema-utils/node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/@develar/schema-utils/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/@electron/asar": {
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.8.tgz",
@ -1666,6 +1704,22 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1676,6 +1730,12 @@
"concat-map": "0.0.1"
}
},
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -4371,6 +4431,15 @@
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/@types/adm-zip": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.5.tgz",
"integrity": "sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -5249,6 +5318,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/adm-zip": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
"integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
"dev": true,
"engines": {
"node": ">=6.0"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -5275,14 +5353,14 @@
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
@ -5290,13 +5368,21 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^6.9.1"
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ansi-colors": {
@ -7707,6 +7793,30 @@
"node": ">=8"
}
},
"node_modules/dmg-license/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"optional": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/dmg-license/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"optional": true
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -7916,6 +8026,15 @@
"node": ">=12"
}
},
"node_modules/electron-log": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.3.tgz",
"integrity": "sha512-jUgAuRjfpCD9tmH1F6fb195YsFfM/DkqkZLhFeo0VAAstantn11bxmgx63uE6KG/JljHG7sIkgM2QEjDimJI0g==",
"dev": true,
"engines": {
"node": ">= 14"
}
},
"node_modules/electron-publish": {
"version": "24.8.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.8.1.tgz",
@ -8802,6 +8921,22 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -8824,6 +8959,12 @@
"node": ">=10.13.0"
}
},
"node_modules/eslint/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/eslint/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -11647,9 +11788,9 @@
"dev": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/json-stable-stringify-without-jsonify": {
@ -17124,28 +17265,6 @@
"node": ">=10.0.0"
}
},
"node_modules/table/node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/table/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",

View file

@ -1,10 +1,13 @@
{
"name": "nuxt-app",
"author": "LDC Service Web",
"description": "Application Kiosk",
"version": "1.0.0",
"name": "app-kiosk",
"private": true,
"type": "commonjs",
"main": "dist-electron/main.js",
"scripts": {
"build": "nuxt build",
"build": "nuxi build --prerender && electron-builder",
"dev": "nuxt dev",
"electron": "nuxt dev",
"generate": "nuxt generate",
@ -28,14 +31,19 @@
"@nuxt/devtools": "^1.0.0-beta.1",
"@nuxtjs/eslint-config-typescript": "latest",
"@popperjs/core": "^2.11.8",
"@types/adm-zip": "^0.5.5",
"@types/bootstrap": "^5.2.10",
"@types/dompurify": "^3.0.3",
"@types/konami-code-js": "^0.8.1",
"@vue/eslint-config-typescript": "^12.0.0",
"adm-zip": "^0.5.10",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"bootstrap": "^5.3.2",
"dompurify": "^3.0.6",
"electron": "^28.1.1",
"electron-builder": "^24.9.1",
"electron-log": "^5.0.3",
"eslint": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-nuxt": "latest",

View file

@ -5,6 +5,8 @@
</template>
<script lang="ts">
import log from "electron-log";
import { defineComponent } from "vue";
import { useRoute, type RouteLocationNormalizedLoaded } from "vue-router";
@ -21,7 +23,7 @@ export default defineComponent({
},
mounted() {
const route = useRoute();
console.log(route);
log.info(route);
},
methods: {
}

View file

@ -12,6 +12,7 @@
</template>
<script lang="ts">
import log from "electron-log";
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
@ -21,7 +22,7 @@ export default defineComponent({
},
mounted() {
const route = useRoute();
console.log(route);
log.info(route);
},
methods: {
}

33
schemas/app.schema.json Normal file
View file

@ -0,0 +1,33 @@
{
"$id": "schemas/app.schema.json",
"title": "Showroom Bo",
"description": "Showroom Bo json data format",
"type": "object",
"properties": {
"version": {
"description": "Version de l'application",
"$ref": "#/$defs/version"
},
"settings": {
"$ref": "settings.schema.json"
},
"items": {
"type": "array",
"items": {
"$ref": "brand.schema.json"
}
}
},
"$defs": {
"version": {
"description": "Version",
"pattern": "^[0-9]+?\\.[0-9]+?\\.[0-9]+?$",
"type": "string"
}
},
"required": [
"version",
"settings",
"items"
]
}

60
schemas/brand.schema.json Normal file
View file

@ -0,0 +1,60 @@
{
"$id": "schemas/brand.schema.json",
"title": "Brand",
"description": "A brand from Showroom Bo",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer",
"minimum": 1
},
"name": {
"description": "Nom de la marque",
"type": "string"
},
"slug": {
"description": "Slug de la marque",
"type": "string"
},
"color": {
"description": "Couleur de la marque",
"$ref": "#/$defs/hexcolor"
},
"picture": {
"description": "Logo de la marque, chemin tel que présent dans le zip",
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
},
"ranges": {
"type": "array",
"items": {
"$ref": "range.schema.json"
}
}
},
"$defs": {
"hexcolor": {
"description": "HEX color",
"pattern": "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$",
"type": "string"
}
},
"required": [
"id",
"name",
"slug",
"color",
"picture",
"created_at",
"updated_at",
"ranges"
]
}

View file

@ -0,0 +1,52 @@
{
"$id": "schemas/product.schema.json",
"title": "Product",
"description": "A product from Showroom Bo",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer",
"minimum": 1
},
"name": {
"description": "Nom du produit",
"type": "string"
},
"slug": {
"description": "Slug du produit",
"type": "string"
},
"description": {
"description": "Description du produit, texte WYSYWYG",
"type": "string"
},
"picture": {
"description": "Logo du produit, chemin tel que présent dans le zip",
"type": "string"
},
"range_id": {
"description": "Clé étrangère de la gamme",
"type": "integer",
"minimum": 1
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"required": [
"id",
"name",
"slug",
"description",
"picture",
"range_id",
"created_at",
"updated_at"
]
}

54
schemas/range.schema.json Normal file
View file

@ -0,0 +1,54 @@
{
"$id": "schemas/range.schema.json",
"title": "Range",
"description": "A range from Showroom Bo",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a range",
"type": "integer",
"minimum": 1
},
"name": {
"description": "Nom de la gamme",
"type": "string"
},
"slug": {
"description": "Slug de la gamme",
"type": "string"
},
"picture": {
"description": "Logo de la gamme, chemin tel que présent dans le zip",
"type": "string"
},
"brand_id": {
"description": "Clé étrangère de la marque",
"type": "integer",
"minimum": 1
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
},
"products": {
"type": "array",
"items": {
"$ref": "product.schema.json"
}
}
},
"required": [
"id",
"name",
"slug",
"picture",
"brand_id",
"created_at",
"updated_at",
"products"
]
}

View file

@ -0,0 +1,39 @@
{
"$id": "schemas/settings.schema.json",
"title": "Settings",
"description": "Settings from Showroom Bo",
"type": "object",
"properties": {
"title": {
"description": "Titre de l'application",
"type": "string"
},
"logo": {
"description": "Logo de l'application, chemin tel que présent dans le zip",
"type": "string"
},
"color": {
"description": "Couleur de l'application",
"$ref": "#/$defs/hexcolor"
},
"items_by_line": {
"description": "Éléments par ligne",
"type": "integer",
"minimum": 3,
"maximum": 6
}
},
"$defs": {
"hexcolor": {
"description": "HEX color",
"pattern": "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$",
"type": "string"
}
},
"required": [
"title",
"logo",
"color",
"items_by_line"
]
}