Compare commits
9 Commits
68756014be
...
b88208ba3d
| Author | SHA1 | Date | |
|---|---|---|---|
| b88208ba3d | |||
| 5ceadd09dd | |||
| 7387f33678 | |||
| f4c704740a | |||
| 0c6cdcf1f5 | |||
| 6bfa971b0a | |||
| 809fa354c4 | |||
| e0efd9dc41 | |||
| ff22127baa |
27
.drone.yml
27
.drone.yml
@@ -36,23 +36,42 @@ trigger:
|
||||
branch:
|
||||
- main
|
||||
|
||||
volumes:
|
||||
- name: wasm
|
||||
temp: {}
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
- name: build wasm
|
||||
image: golang:1.17
|
||||
environment:
|
||||
GOOS: js
|
||||
GOARCH: wasm
|
||||
volumes:
|
||||
- name: wasm
|
||||
path: /wasm
|
||||
commands:
|
||||
- make wasm
|
||||
- "cp covergen.wasm /wasm/covergen.wasm"
|
||||
# Grab the wasm shim from the docker image, otherwise there is a mismatch
|
||||
- "cp /usr/local/go/misc/wasm/wasm_exec.js assets/wasm_exec.js"
|
||||
- "cp /usr/local/go/misc/wasm/wasm_exec.js /wasm/wasm_exec.js"
|
||||
|
||||
- name: build frontend
|
||||
image: node:16
|
||||
volumes:
|
||||
- name: wasm
|
||||
path: /wasm
|
||||
commands:
|
||||
- cd frontend
|
||||
- yarn install
|
||||
- "cp /wasm/* public/"
|
||||
- yarn build
|
||||
- name: upload
|
||||
image: plugins/s3
|
||||
settings:
|
||||
bucket: covergen
|
||||
source: assets/**/*
|
||||
source: frontend/dist/**/*
|
||||
target: /
|
||||
strip_prefix: assets/
|
||||
strip_prefix: frontend/dist/
|
||||
|
||||
path_style: true
|
||||
endpoint: https://s3.blacknova.io
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
covergen
|
||||
!covergen/
|
||||
*.pdf
|
||||
assets/*.wasm
|
||||
*.wasm
|
||||
dist/
|
||||
2
Makefile
2
Makefile
@@ -6,4 +6,4 @@ build-cross-clis:
|
||||
|
||||
.PHONY: wasm
|
||||
wasm:
|
||||
GOOS=js GOARCH=wasm go build -o assets/covergen.wasm ./cmd/wasm/main.go
|
||||
GOOS=js GOARCH=wasm go build -o covergen.wasm ./cmd/wasm/main.go
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="wasm_exec.js"></script>
|
||||
<script>
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch("covergen.wasm"), go.importObject).then((result) => {
|
||||
go.run(result.instance);
|
||||
});
|
||||
|
||||
function makeCover(args) {
|
||||
const result = window.generateCover(args);
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return new File([result], 'cover.pdf', {type: 'application/pdf'});
|
||||
}
|
||||
|
||||
function makeSplitCover(args) {
|
||||
const result = window.generateSplitCover(args);
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return Object.fromEntries(['front', 'back'].map((side) => [side, new File([result[side]], `${side}.pdf`, {type: 'application/pdf'})]));
|
||||
}
|
||||
|
||||
function letsfuckinggo() {
|
||||
const covers = makeSplitCover({
|
||||
customer: document.getElementById('customer').value,
|
||||
number: document.getElementById('number').value,
|
||||
numberPrefix: document.getElementById('prefix').value,
|
||||
hlColor: document.getElementById('color').value,
|
||||
})
|
||||
|
||||
document.getElementById('front').src = window.URL.createObjectURL(covers.front);
|
||||
document.getElementById('back').src = window.URL.createObjectURL(covers.back);
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.covers {
|
||||
height: 100%;
|
||||
width: 90%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.covers iframe {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<label>
|
||||
Customer:
|
||||
<textarea id="customer"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
Prefix:
|
||||
<input id="prefix" type="text" value="offerte">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
Number:
|
||||
<input id="number" type="text">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
Color:
|
||||
<input id="color" type="color" value="#FF69B4">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="letsfuckinggo()">Fuck it!</button>
|
||||
</div>
|
||||
<div class="covers">
|
||||
<iframe id="front"></iframe>
|
||||
<iframe id="back"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="download-links">
|
||||
Download the CLI:
|
||||
<a href="./dist/covergen.linux-amd64">Linux amd64</a>
|
||||
<a href="./dist/covergen.darwin-amd64">Darwin amd64</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
12
frontend/.eslintrc
Normal file
12
frontend/.eslintrc
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:vue/vue3-recommended",
|
||||
"plugin:prettier-vue/recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": [0],
|
||||
"vue/multi-word-component-names": 0
|
||||
}
|
||||
}
|
||||
8
frontend/.gitignore
vendored
Normal file
8
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
.yarn/install-state.gz
|
||||
public/*.wasm
|
||||
3
frontend/.prettierrc
Normal file
3
frontend/.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
11
frontend/README.md
Normal file
11
frontend/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Vue 3 + Typescript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
|
||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette.
|
||||
16
frontend/index.html
Normal file
16
frontend/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
<title>NVLS | Covergen</title>
|
||||
</head>
|
||||
<body class="bg-gray-200">
|
||||
<div id="app"></div>
|
||||
<script src="/wasm_exec.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
37
frontend/package.json
Normal file
37
frontend/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "yarn lint:ts && yarn lint:style",
|
||||
"lint:ts": "vue-tsc --noEmit",
|
||||
"lint:style": "eslint --ext .ts,.vue --ignore-path .gitignore .",
|
||||
"lint:fix": "yarn lint:style --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
"pinia": "^2.0.9",
|
||||
"tailwindcss": "^3.0.15",
|
||||
"vue": "^3.2.25"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/golang-wasm-exec": "^1.15.0",
|
||||
"@types/node": "^17.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"@vitejs/plugin-vue": "^2.0.0",
|
||||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier-vue": "^3.1.0",
|
||||
"eslint-plugin-vue": "^8.3.0",
|
||||
"postcss": "^8.4.5",
|
||||
"prettier": "^2.5.1",
|
||||
"typescript": "^4.4.4",
|
||||
"vite": "^2.7.2",
|
||||
"vue-tsc": "^0.29.8"
|
||||
}
|
||||
}
|
||||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
frontend/public/favicon.ico
Normal file
BIN
frontend/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
18
frontend/src/App.vue
Normal file
18
frontend/src/App.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import Nav from './components/Nav.vue';
|
||||
import Settings from './components/Settings.vue';
|
||||
import Renderer from './components/Renderer.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Nav />
|
||||
<div class="flex grow space-x-8">
|
||||
<div class="w-80">
|
||||
<Settings />
|
||||
</div>
|
||||
<Renderer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
BIN
frontend/src/assets/logo.png
Normal file
BIN
frontend/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
35
frontend/src/components/Nav.vue
Normal file
35
frontend/src/components/Nav.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
const downloads = [
|
||||
{ name: 'Linux', link: './dist/covergen.linux-amd64' },
|
||||
{ name: 'Darwin', link: './dist/covergen.darwin-amd64' },
|
||||
];
|
||||
</script>
|
||||
<template>
|
||||
<div class="w-full mx-auto px-8">
|
||||
<div class="relative flex items-center justify-between h-16">
|
||||
<div class="flex-1 flex items-stretch justify-between">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
<img
|
||||
src="../assets/logo.png"
|
||||
alt="covergen logo"
|
||||
class="h-8 w-auto mr-2"
|
||||
/>
|
||||
<h1>CoverGen</h1>
|
||||
</div>
|
||||
|
||||
<div class="ml-6">
|
||||
<div class="flex space-x-4 items-center text-sm font-medium">
|
||||
<span class="text-base"> Download me: </span>
|
||||
<a
|
||||
v-for="dl in downloads"
|
||||
:key="dl.link"
|
||||
:href="dl.link"
|
||||
class="text-gray-800 hover:bg-gray-300 px-3 py-2 rounded-md"
|
||||
>{{ dl.name }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
25
frontend/src/components/Renderer.vue
Normal file
25
frontend/src/components/Renderer.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { useCover } from '@/stores/cover';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const store = useCover();
|
||||
const { frontUri, backUri } = storeToRefs(store);
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex flex-row grow">
|
||||
<div class="grow p-4">
|
||||
<h2 class="text-lg font-medium pb-4">Front</h2>
|
||||
<iframe
|
||||
:src="frontUri"
|
||||
class="w-full aspect-A4 border border-slate-500 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="grow p-4">
|
||||
<h2 class="text-lg font-medium pb-4">Back</h2>
|
||||
<iframe
|
||||
:src="backUri"
|
||||
class="w-full aspect-A4 border border-slate-500 rounded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
70
frontend/src/components/Settings.vue
Normal file
70
frontend/src/components/Settings.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import Input from '@/components/form/Input.vue';
|
||||
import TextArea from '@/components/form/TextArea.vue';
|
||||
import Color from '@/components/form/Color.vue';
|
||||
import SadFace from '@/icons/SadFace.vue';
|
||||
import { randomLabel } from '@/lib/randomlabel';
|
||||
import { useCover } from '@/stores/cover';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const store = useCover();
|
||||
const { customer, prefix, color, number } = storeToRefs(store);
|
||||
|
||||
const possibleLabels = [
|
||||
'Hit it!',
|
||||
'Let it rain!',
|
||||
'💰💰💰',
|
||||
'🤑🤑🤑',
|
||||
'Make Rutte Proud',
|
||||
'Kaching!',
|
||||
'Rosebud',
|
||||
'You show that customer',
|
||||
'Do it for Berend',
|
||||
];
|
||||
const label = randomLabel(possibleLabels);
|
||||
|
||||
const renderError = ref<string | null>(null);
|
||||
|
||||
function doRender() {
|
||||
try {
|
||||
renderError.value = null;
|
||||
store.render();
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
renderError.value = e.message;
|
||||
} else if (typeof e === 'string') {
|
||||
renderError.value = e;
|
||||
} else {
|
||||
renderError.value = 'An unknown error occurred';
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 flex flex-col gap-4">
|
||||
<h2 class="text-lg font-medium">Settings</h2>
|
||||
<TextArea v-model="customer" label="Customer" />
|
||||
<Input v-model="prefix" label="Prefix" />
|
||||
<Input v-model="number" label="Number" />
|
||||
<Color v-model="color" label="Highlight Color" />
|
||||
|
||||
<div
|
||||
v-if="renderError !== null"
|
||||
class="flex bg-red-100 rounded-lg p-4 mb-4 text-sm text-red-700 items-center gap-2"
|
||||
role="alert"
|
||||
>
|
||||
<SadFace />
|
||||
<div><span class="font-medium">Error:</span> {{ renderError }}</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="px-4 py-2 bg-blue-500 rounded-lg hover:bg-blue-600 text-white border-2 active:border-blue-500 focus:outline focus:outline-2 focus:outline-blue-500"
|
||||
@click="label.update() && doRender()"
|
||||
>
|
||||
{{ label.label.value }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
30
frontend/src/components/form/Color.vue
Normal file
30
frontend/src/components/form/Color.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
modelValue: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">{{ label }}</label>
|
||||
<div class="mt-1 relative rounded-md shadow-sm">
|
||||
<input
|
||||
v-model="localValue"
|
||||
type="color"
|
||||
class="block w-full py-2 px-4 h-10 bg-white border border-gray-300 hover:border-2 hover:border-blue-500 hover:cursor-pointer focus:border-blue-500 rounded-md"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
30
frontend/src/components/form/Input.vue
Normal file
30
frontend/src/components/form/Input.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
modelValue: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">{{ label }}</label>
|
||||
<div class="mt-1 relative rounded-md shadow-sm">
|
||||
<input
|
||||
v-model="localValue"
|
||||
type="text"
|
||||
class="focus:ring-blue-500 focus:border-blue-500 block w-full border-gray-300 rounded-md"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
29
frontend/src/components/form/TextArea.vue
Normal file
29
frontend/src/components/form/TextArea.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
label: string;
|
||||
modelValue: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">{{ label }}</label>
|
||||
<div class="mt-1 relative rounded-md shadow-sm">
|
||||
<textarea
|
||||
v-model="localValue"
|
||||
class="focus:ring-blue-500 focus:border-blue-500 block w-full border-gray-300 rounded-md"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
8
frontend/src/env.d.ts
vendored
Normal file
8
frontend/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
16
frontend/src/icons/SadFace.vue
Normal file
16
frontend/src/icons/SadFace.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
14
frontend/src/index.css
Normal file
14
frontend/src/index.css
Normal file
@@ -0,0 +1,14 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
}
|
||||
51
frontend/src/lib/covergen.ts
Normal file
51
frontend/src/lib/covergen.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
interface CoverArgs {
|
||||
customer: string;
|
||||
number: string;
|
||||
numberPrefix?: string;
|
||||
hlColor?: string;
|
||||
}
|
||||
|
||||
interface CoverError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
generateCover(args: CoverArgs): Uint8Array | CoverError;
|
||||
|
||||
generateSplitCover(
|
||||
args: CoverArgs
|
||||
): { front: Uint8Array; back: Uint8Array } | CoverError;
|
||||
}
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch('covergen.wasm'), go.importObject).then(
|
||||
(result) => {
|
||||
go.run(result.instance);
|
||||
}
|
||||
);
|
||||
|
||||
export function generateCover(args: CoverArgs): File {
|
||||
const result = window.generateCover(args);
|
||||
if ('error' in result) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return new File([result], 'cover.pdf', { type: 'application/pdf' });
|
||||
}
|
||||
|
||||
export function generateSplitCover(args: CoverArgs): {
|
||||
front: File;
|
||||
back: File;
|
||||
} {
|
||||
const result = window.generateSplitCover(args);
|
||||
if ('error' in result) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
return {
|
||||
front: new File([result.front], 'front.pdf', { type: 'application/pdf' }),
|
||||
back: new File([result.back], 'back.pdf', { type: 'application/pdf' }),
|
||||
};
|
||||
}
|
||||
18
frontend/src/lib/randomlabel.ts
Normal file
18
frontend/src/lib/randomlabel.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { readonly, ref } from 'vue';
|
||||
|
||||
export function randomLabel(options: string[]) {
|
||||
const label = ref<string>();
|
||||
const randomLabel = () => {
|
||||
let newLabel;
|
||||
do {
|
||||
newLabel = options[Math.floor(Math.random() * options.length)];
|
||||
} while (newLabel === label.value);
|
||||
return newLabel;
|
||||
};
|
||||
label.value = randomLabel();
|
||||
|
||||
return {
|
||||
label: readonly(label),
|
||||
update: () => (label.value = randomLabel()),
|
||||
};
|
||||
}
|
||||
7
frontend/src/main.ts
Normal file
7
frontend/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import './index.css';
|
||||
import { createPinia } from 'pinia';
|
||||
import './lib/covergen'; // Get that go app booting
|
||||
|
||||
createApp(App).use(createPinia()).mount('#app');
|
||||
28
frontend/src/stores/cover.ts
Normal file
28
frontend/src/stores/cover.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { generateSplitCover } from '@/lib/covergen';
|
||||
|
||||
export const useCover = defineStore('coverSettings', {
|
||||
state() {
|
||||
return {
|
||||
customer: '',
|
||||
prefix: 'offerte',
|
||||
number: '',
|
||||
color: '#ff00ff',
|
||||
|
||||
frontUri: '',
|
||||
backUri: '',
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
render() {
|
||||
const { front, back } = generateSplitCover({
|
||||
customer: this.customer,
|
||||
numberPrefix: this.prefix,
|
||||
number: this.number,
|
||||
hlColor: this.color,
|
||||
});
|
||||
this.frontUri = URL.createObjectURL(front);
|
||||
this.backUri = URL.createObjectURL(back);
|
||||
},
|
||||
},
|
||||
});
|
||||
21
frontend/tailwind.config.js
Normal file
21
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
aspectRatio: {
|
||||
'A4': '1 / 1.4142',
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
||||
18
frontend/tsconfig.json
Normal file
18
frontend/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
14
frontend/vite.config.ts
Normal file
14
frontend/vite.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { resolve } from 'path';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => ({
|
||||
plugins: [vue()],
|
||||
base: mode === 'production' ? '/covergen/' : '/',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
3710
frontend/yarn.lock
Normal file
3710
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user