|
|
<script lang="ts">
|
|
|
import Button from "$lib/header/Button.svelte";
|
|
|
import { onMount } from "svelte";
|
|
|
import words from "$lib/en-lang.json";
|
|
|
|
|
|
let total = 0;
|
|
|
let error = null;
|
|
|
let lang = words["calculator"];
|
|
|
let types = lang["types"];
|
|
|
|
|
|
interface Commission {
|
|
|
type: string,
|
|
|
name: string,
|
|
|
price: number,
|
|
|
example: string,
|
|
|
questions?: any[],
|
|
|
answers?: any[],
|
|
|
}
|
|
|
|
|
|
let commissions: Commission[] = [];
|
|
|
|
|
|
let number: number;
|
|
|
$: number = commissions.length + 1;
|
|
|
|
|
|
async function setup() {
|
|
|
let prefill = true;
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
urlParams.forEach((value, key) => {
|
|
|
if(key == "commissions") {
|
|
|
try {
|
|
|
prefill = false;
|
|
|
commissions = JSON.parse(value);
|
|
|
commissions.forEach(value => {
|
|
|
types.forEach(element => {
|
|
|
if(element["name"] == value.type) {
|
|
|
value['questions'] = element["questions"];
|
|
|
}
|
|
|
});
|
|
|
update_price(value);
|
|
|
update_example(value);
|
|
|
});
|
|
|
} catch(msg) {
|
|
|
prefill = true;
|
|
|
error = "The selected preset link failed to load. It may be from an old version of the commission picker. It has been replaced with the default preset below."
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
if(prefill) {
|
|
|
base();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function update_example(commission: Commission) {
|
|
|
let lastQuestion = commission.questions[commission.questions.length - 1];
|
|
|
let lastAnswer = commission.answers[commission.answers.length - 1];
|
|
|
let example = "";
|
|
|
commission.example = "";
|
|
|
let typeFound = types.filter(value => {
|
|
|
return value['name'] == commission.type;
|
|
|
});
|
|
|
let type = typeFound[0];
|
|
|
|
|
|
let count = 0;
|
|
|
commission.questions.forEach(value => {
|
|
|
if(type['examples_from'] == value['name']) {
|
|
|
lastQuestion = commission.questions[count];
|
|
|
lastAnswer = commission.answers[count];
|
|
|
}
|
|
|
count++;
|
|
|
})
|
|
|
|
|
|
lastQuestion.options.forEach(value => {
|
|
|
if(value['name'] == lastAnswer['option']) {
|
|
|
example = value['example'];
|
|
|
}
|
|
|
});
|
|
|
|
|
|
type['examples'].forEach(value => {
|
|
|
if(value['name'] == example) {
|
|
|
commission.example = value['url'];
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function base() {
|
|
|
let commission = {
|
|
|
"type": types[0]["name"],
|
|
|
"name": "Commission " + number,
|
|
|
"example": types[0]["example"],
|
|
|
"price": 10,
|
|
|
};
|
|
|
commissions.push(commission);
|
|
|
update_commission_type(commission);
|
|
|
update_example(commission);
|
|
|
update_price(commission);
|
|
|
commissions = commissions;
|
|
|
}
|
|
|
|
|
|
function save() {
|
|
|
let data = [];
|
|
|
commissions.forEach(value => {
|
|
|
data.push({
|
|
|
"type": value['type'],
|
|
|
"name": value["name"],
|
|
|
"answers": value["answers"]
|
|
|
})
|
|
|
});
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
urlParams.set("commissions", JSON.stringify(data));
|
|
|
|
|
|
let url = window.location.origin + window.location.pathname + "?" + urlParams.toString();
|
|
|
|
|
|
window.history.replaceState( {} , document.title, url);
|
|
|
}
|
|
|
|
|
|
function save_clipboard() {
|
|
|
save();
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
let url = window.location.origin + window.location.pathname + "?" + urlParams.toString();
|
|
|
navigator.clipboard.writeText(url);
|
|
|
}
|
|
|
|
|
|
function update_commission_type(commission: Commission) {
|
|
|
types.forEach(element => {
|
|
|
if(element["name"] == commission.type) {
|
|
|
commission.questions = element["questions"];
|
|
|
}
|
|
|
});
|
|
|
commission.answers = [];
|
|
|
commission.questions.forEach(value => {
|
|
|
commission.answers.push({'name': value.name, 'option': value.options[0]['name']});
|
|
|
});
|
|
|
update_price(commission);
|
|
|
update_example(commission);
|
|
|
}
|
|
|
|
|
|
function update_price(commission: Commission) {
|
|
|
commission.price = 0;
|
|
|
|
|
|
for (let index = 0; index < commission.answers.length; index++) {
|
|
|
const answer = commission.answers[index];
|
|
|
const question = commission.questions.filter(value => {
|
|
|
return value['name'] == answer['name'];
|
|
|
})[0];
|
|
|
|
|
|
question['options'].forEach(value => {
|
|
|
if(value['name'] == answer['option']) {
|
|
|
commission.price += value['price'];
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
total = 0;
|
|
|
commissions.forEach(value => {
|
|
|
total += value.price;
|
|
|
})
|
|
|
}
|
|
|
|
|
|
async function load() {
|
|
|
let url = await navigator.clipboard.readText();
|
|
|
window.history.replaceState( {} , document.title, url);
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
urlParams.forEach((value, key) => {
|
|
|
if(key == "commissions") {
|
|
|
try {
|
|
|
commissions = JSON.parse(value);
|
|
|
commissions.forEach(value => {
|
|
|
types.forEach(element => {
|
|
|
if(element["name"] == value.type) {
|
|
|
value['questions'] = element["questions"];
|
|
|
}
|
|
|
});
|
|
|
update_price(value);
|
|
|
update_example(value);
|
|
|
});
|
|
|
} catch(msg) {
|
|
|
error = "The selected preset link failed to load. It may be from an old version of the commission picker."
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function createNew() {
|
|
|
base();
|
|
|
save();
|
|
|
}
|
|
|
|
|
|
function copy(commission: Commission) {
|
|
|
let newCommission = { ...commission };
|
|
|
newCommission.name = "Commission " + number;
|
|
|
commissions.push(newCommission);
|
|
|
commissions = commissions;
|
|
|
update_price(newCommission);
|
|
|
update_example(newCommission);
|
|
|
save();
|
|
|
}
|
|
|
|
|
|
function remove(name: string) {
|
|
|
commissions = commissions.filter(value => {
|
|
|
return value.name != name;
|
|
|
});
|
|
|
update_price(commissions[0]);
|
|
|
save();
|
|
|
}
|
|
|
|
|
|
function clear() {
|
|
|
commissions = [];
|
|
|
total = 0;
|
|
|
save();
|
|
|
}
|
|
|
|
|
|
onMount(() => {
|
|
|
setup();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<main class="container flex flex-col gap-5">
|
|
|
<section class="container bg-slate-800 rounded-lg shadow-lg p-3 flex flex-col w-full gap-5">
|
|
|
<nav class="flex flex-col lg:flex-row gap-3 justify-between justify-items-stretch items-center">
|
|
|
<Button href="/">⬅️ Back to home</Button>
|
|
|
<Button href="/commissions">🎨 Visit commissions page</Button>
|
|
|
<Button href="/commissions/tos">⚖️ Read Terms of Service</Button>
|
|
|
</nav>
|
|
|
</section>
|
|
|
|
|
|
<section class="container bg-slate-800 rounded-lg shadow-lg p-5 flex flex-col w-full gap-10">
|
|
|
<div class="flex flex-col lg:flex-row gap-5 lg:gap-1 flex-wrap -ml-2 -mr-2">
|
|
|
<span class="flex-grow"><Button added_classes="w-full" on:click={createNew}>➕ Add Commission</Button></span>
|
|
|
<span><Button on:click={save_clipboard}>💾 Save to Clipboard</Button></span>
|
|
|
<span><Button on:click={load}>📋 Load from Clipboard</Button></span>
|
|
|
<span><Button on:click={clear}>❌ Clear</Button></span>
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
<div class="flex items-center gap-5">
|
|
|
<h3 class="hidden lg:block text-6xl">🧮</h3>
|
|
|
<div class="flex flex-col gap-2">
|
|
|
<div class="flex gap-3 items-center">
|
|
|
<p class="font-bold bg-green-500 py-1 px-5 rounded-lg text-lg">${total}</p>
|
|
|
<h4 class="text-xl font-bold">Estimated Total</h4>
|
|
|
</div>
|
|
|
<p class="italic text-gray-200 text-sm">This figure is an estimate, the final price may differ.<br/>All commissions are strictly SFW only.</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
{#if error}
|
|
|
<section class="bg-slate-800 text-yellow-500 font-bold rounded-lg shadow-lg p-5 flex flex-col gap-5 text-center lg:text-left">
|
|
|
⚠️ {error}
|
|
|
</section>
|
|
|
{/if}
|
|
|
|
|
|
{#if commissions.length == 0}
|
|
|
<section class="bg-slate-800 rounded-lg shadow-lg p-5 flex flex-col lg:flex-row justify-between items-center w-full gap-5">
|
|
|
<div class="flex flex-col text-center lg:text-left">
|
|
|
<p class="text-green-500 font-bold text-xl">It's alone here :'(</p>
|
|
|
<p>You currently have no commissions selected. <span class="hidden lg:block">Add one --></span></p>
|
|
|
</div>
|
|
|
<span><Button on:click={createNew}>➕ Add Commission</Button></span>
|
|
|
</section>
|
|
|
{/if}
|
|
|
|
|
|
{#if commissions.length > 10}
|
|
|
<section class="bg-slate-800 rounded-lg shadow-lg p-5 flex items-end w-full gap-5 text-center lg:text-left">
|
|
|
<p><span class="text-green-500 font-bold text-xl">Calm down Elon Musk!</span>
|
|
|
Your list is becoming quite long. Loading and saving long lists is not supported and may cause considerable lag or result in loss of data.</p>
|
|
|
</section>
|
|
|
{/if}
|
|
|
|
|
|
{#each [...commissions].reverse() as commission}
|
|
|
<section class="flex flex-col lg:flex-row gap-5">
|
|
|
<div class="w-full lg:w-3/5">
|
|
|
<div class="bg-slate-800 rounded-lg shadow-lg p-5 flex flex-col gap-5">
|
|
|
<div class="flex justify-between flex-col lg:flex-row gap-5 lg:gap-0">
|
|
|
<h3 class="text-xl font-bold flex gap-5">{commission.name}<p class="bg-green-500 py-1 text-sm px-5 rounded-lg">${commission.price}</p></h3>
|
|
|
<span>
|
|
|
<Button on:click={() => {copy(commission)}}>⤴️</Button>
|
|
|
<Button on:click={() => {remove(commission.name)}}>🗑️</Button>
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
<form class="flex flex-col gap-10" on:change={() => {update_example(commission)}}>
|
|
|
<div class="flex flex-col gap-1">
|
|
|
<label for={commission.type}>Type of commission</label>
|
|
|
<select class="bg-slate-600 p-2 rounded-lg hover:bg-slate-500 duration-300" bind:value={commission.type} on:change={() => {update_commission_type(commission)}}>
|
|
|
{#each types as question}
|
|
|
<option value={question.name}>{question.name}</option>
|
|
|
{/each}
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
|
{#each commission.questions as question, key}
|
|
|
{#if question.multiple}
|
|
|
<div class="flex flex-col gap-1">
|
|
|
<label for={commission.answers[key]['option']}>{question.name}</label>
|
|
|
{#each commission.answers.filter(value => { return value['name'] == question.name; }) as answer, key}
|
|
|
<div class="flex gap-1">
|
|
|
<select class="flex-grow bg-slate-600 p-2 rounded-lg hover:bg-slate-500 duration-300" bind:value={answer['option']} on:change={() => {update_price(commission)}}>
|
|
|
{#each question.options as option}
|
|
|
<option value={option.name}>{option.name} | ${option.price}</option>
|
|
|
{/each}
|
|
|
</select>
|
|
|
</div>
|
|
|
{/each}
|
|
|
<div class="flex gap-1 w-full items-stretch">
|
|
|
<button type="none" class="w-full bg-slate-600 p-2 rounded-lg hover:bg-slate-500 duration-300" on:click|preventDefault={() => {commission.answers.push({'name': question['name'], 'option': question.options[0]['name']}); commission.answers = commission.answers;}}>Add</button>
|
|
|
{#if commission.answers.filter(value => { return value['name'] == question.name; }).length > 1}
|
|
|
<button type="none" class="w-full bg-slate-600 p-2 rounded-lg hover:bg-slate-500 duration-300" on:click|preventDefault={() => {commission.answers.pop(); commission.answers = commission.answers; }}>Remove</button>
|
|
|
{/if}
|
|
|
</div>
|
|
|
</div>
|
|
|
{:else}
|
|
|
<div class="flex flex-col gap-1">
|
|
|
<label for={commission.answers[key]['option']}>{question.name}</label>
|
|
|
<select class="bg-slate-600 p-2 rounded-lg hover:bg-slate-500 duration-300" bind:value={commission.answers[key]['option']} on:change={() => {update_price(commission)}}>
|
|
|
{#each question.options as option}
|
|
|
<option value={option.name}>{option.name} | ${option.price}</option>
|
|
|
{/each}
|
|
|
</select>
|
|
|
</div>
|
|
|
{/if}
|
|
|
{/each}
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="w-full lg:w-2/5">
|
|
|
{#if commission.example != ""}
|
|
|
<div class="relative bg-black bg-opacity-50 rounded-xl shadow-xl">
|
|
|
<p class="xl:block hidden absolute top-0 right-0 text-right -rotate-12 font-bold text-4xl -mb-40 -mr-40">
|
|
|
🠔 Example!
|
|
|
</p>
|
|
|
<img src={commission.example} alt="example" class="rounded-xl shadow-xl w-full" />
|
|
|
</div>
|
|
|
{:else}
|
|
|
<div class="relative w-full aspect-square bg-black bg-opacity-50 rounded-xl shadow-xl">
|
|
|
<p class="w-full h-full absolute inset-0 flex items-center justify-center text-center opacity-50 -rotate-45 font-bold text-6xl">
|
|
|
NO EXAMPLE AVAILABLE
|
|
|
</p>
|
|
|
</div>
|
|
|
{/if}
|
|
|
</div>
|
|
|
</section>
|
|
|
{/each}
|
|
|
</main> |