Compare commits
4 Commits
feat/front
...
chore/mini
| Author | SHA1 | Date | |
|---|---|---|---|
| b59c939e3e | |||
| 3a30c7a6f4 | |||
| bdab11b100 | |||
| d3f6f0e946 |
37
.drone.yml
37
.drone.yml
@@ -3,10 +3,6 @@ kind: pipeline
|
|||||||
type: kubernetes
|
type: kubernetes
|
||||||
name: build-clis
|
name: build-clis
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: golang:1.17
|
image: golang:1.17
|
||||||
@@ -19,6 +15,7 @@ steps:
|
|||||||
source: dist/*
|
source: dist/*
|
||||||
target: /dist/
|
target: /dist/
|
||||||
strip_prefix: dist/
|
strip_prefix: dist/
|
||||||
|
acl: readonly
|
||||||
|
|
||||||
path_style: true
|
path_style: true
|
||||||
endpoint: https://s3.blacknova.io
|
endpoint: https://s3.blacknova.io
|
||||||
@@ -32,46 +29,22 @@ kind: pipeline
|
|||||||
type: kubernetes
|
type: kubernetes
|
||||||
name: build-wasm
|
name: build-wasm
|
||||||
|
|
||||||
#trigger:
|
|
||||||
# branch:
|
|
||||||
# - main
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: wasm
|
|
||||||
temp: {}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build wasm
|
- name: build
|
||||||
image: golang:1.17
|
image: golang:1.17
|
||||||
environment:
|
environment:
|
||||||
GOOS: js
|
GOOS: js
|
||||||
GOARCH: wasm
|
GOARCH: wasm
|
||||||
volumes:
|
|
||||||
- name: wasm
|
|
||||||
path: /wasm
|
|
||||||
commands:
|
commands:
|
||||||
- make wasm
|
- 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 /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
|
- name: upload
|
||||||
image: plugins/s3
|
image: plugins/s3
|
||||||
settings:
|
settings:
|
||||||
bucket: covergen
|
bucket: covergen
|
||||||
source: frontend/dist/**/*
|
source: assets/**/*
|
||||||
target: /
|
target: /
|
||||||
strip_prefix: frontend/dist/
|
strip_prefix: assets/
|
||||||
|
acl: readonly
|
||||||
|
|
||||||
path_style: true
|
path_style: true
|
||||||
endpoint: https://s3.blacknova.io
|
endpoint: https://s3.blacknova.io
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
covergen
|
covergen
|
||||||
!covergen/
|
!covergen/
|
||||||
*.pdf
|
*.pdf
|
||||||
*.wasm
|
assets/*.wasm
|
||||||
dist/
|
dist/
|
||||||
2
Makefile
2
Makefile
@@ -6,4 +6,4 @@ build-cross-clis:
|
|||||||
|
|
||||||
.PHONY: wasm
|
.PHONY: wasm
|
||||||
wasm:
|
wasm:
|
||||||
GOOS=js GOARCH=wasm go build -o covergen.wasm ./cmd/wasm/main.go
|
GOOS=js GOARCH=wasm go build -o assets/covergen.wasm ./cmd/wasm/main.go
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -1,20 +0,0 @@
|
|||||||
# Covergen
|
|
||||||
|
|
||||||
Just generates some magic PDF invoice covers using straight up dark magic.
|
|
||||||
|
|
||||||
## Just be lazy
|
|
||||||
|
|
||||||
Go see it in action and download it from [https://s3.blacknova.io/covergen/index.html](https://s3.blacknova.io/covergen/index.html)
|
|
||||||
|
|
||||||
## Just build it
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go build -o covergen ./cmd/covergen
|
|
||||||
```
|
|
||||||
|
|
||||||
## It has wasm!
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make wasm
|
|
||||||
```
|
|
||||||
|
|
||||||
93
assets/index.html
Normal file
93
assets/index.html
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<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>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"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
8
frontend/.gitignore
vendored
@@ -1,8 +0,0 @@
|
|||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
.yarn/install-state.gz
|
|
||||||
public/*.wasm
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,18 +0,0 @@
|
|||||||
<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>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<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
8
frontend/src/env.d.ts
vendored
@@ -1,8 +0,0 @@
|
|||||||
/// <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;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
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' }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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');
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
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);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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'),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"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"]
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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
3710
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user