You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
16 KiB
TypeScript
438 lines
16 KiB
TypeScript
import {
|
|
Component,
|
|
createEffect,
|
|
createMemo,
|
|
onCleanup,
|
|
onMount,
|
|
useContext,
|
|
} from "solid-js";
|
|
import LufraiLogo from "~icons/custom/lufrai-logo";
|
|
import AppIcon from "~icons/custom/icon";
|
|
import ExternalLinkIcon from "~icons/carbon/launch";
|
|
import WarningIcon from "~icons/carbon/warning-alt-filled";
|
|
import LaunchIcon from "~icons/carbon/edit";
|
|
import DonationIcon from "~icons/carbon/favorite-filled";
|
|
import WatermarkIcon from "~icons/carbon/bullhorn";
|
|
import Modal, { ModalCloseButton } from "./Modal";
|
|
import { LocalStoreContext } from "~/stores";
|
|
import createAccordion from "./Accordion";
|
|
import typer from "typer-js";
|
|
import "typer-js/dist/typer.min.css";
|
|
import { getDisplayDate, getHost, shuffle } from "~/util";
|
|
import { capitalize } from "froebel";
|
|
|
|
export const description =
|
|
"Räppli ist eine freie Web App zur Erstellung von Schweizerischen Rechnungen inklusive QR-Code. Erfasse deine Rechnungspositionen und erhalte unmittelbar eine druckbare Rechnung.";
|
|
|
|
const WelcomeModal: Component = (props) => {
|
|
const [localState, setLocalState, localStateMounted] =
|
|
useContext(LocalStoreContext)!;
|
|
const [AcordionItem] = createAccordion();
|
|
let subtitleEl: HTMLSpanElement = undefined!;
|
|
const isOpen = createMemo(() => {
|
|
return localStateMounted() && localState.showWelcome;
|
|
});
|
|
|
|
onMount(function () {
|
|
let adjectives = [
|
|
"Schweizerischen",
|
|
"anständigen",
|
|
"umfassenden",
|
|
"erfrischenden",
|
|
"erfreulichen",
|
|
"sauberen",
|
|
"übersichtlichen",
|
|
];
|
|
let nouns = ["Rechnung", "Auftragsbestätigung", "Offerte"];
|
|
|
|
let combinations: [string, string][] = [];
|
|
for (const adjective of adjectives) {
|
|
for (const noun of nouns) {
|
|
combinations.push([adjective, noun]);
|
|
}
|
|
}
|
|
const firstCombination = combinations.splice(0, 1)[0];
|
|
combinations = shuffle(combinations);
|
|
combinations.unshift(firstCombination!);
|
|
|
|
let typerInstance = typer(subtitleEl, { min: 60, max: 160 });
|
|
|
|
let lastAdjective = "";
|
|
let lastNoun = "";
|
|
for (const [adjective, noun] of combinations) {
|
|
const first = lastAdjective === "";
|
|
|
|
if (lastAdjective === adjective) {
|
|
typerInstance.back(lastNoun.length, 80).continue(noun);
|
|
} else {
|
|
const method = first ? "line" : "continue";
|
|
typerInstance
|
|
.back("all", 65)
|
|
[method](
|
|
`${adjective} ${noun}`,
|
|
first ? { min: 60, max: 70 } : undefined
|
|
);
|
|
}
|
|
|
|
typerInstance.pause(first ? 4181 : 6765);
|
|
|
|
lastNoun = noun;
|
|
lastAdjective = adjective;
|
|
}
|
|
|
|
typerInstance.back("all", 50).repeat(Infinity);
|
|
|
|
let halted = false;
|
|
|
|
createEffect(function () {
|
|
if (!isOpen()) {
|
|
halted = typerInstance.halt() === undefined;
|
|
} else {
|
|
try {
|
|
// typer-js has some weird logic how it handles halt / resume. Essentially if halt wasn't executed before of resume, resume breaks completely
|
|
halted && typerInstance.resume();
|
|
} catch (err) {
|
|
if ((err as TypeError).message != "u.resume is not a function") {
|
|
console.dir(err);
|
|
}
|
|
}
|
|
halted = false;
|
|
}
|
|
});
|
|
|
|
onCleanup(function () {
|
|
typerInstance.kill();
|
|
});
|
|
});
|
|
|
|
const ShareonLink: Component<{ provider: string; via?: string }> = (
|
|
props
|
|
) => (
|
|
<a
|
|
class={props.provider + " shadow"}
|
|
title={capitalize(props.provider)}
|
|
data-via={props.via}
|
|
aria-label={`Share on ${capitalize(props.provider)}`}
|
|
></a>
|
|
);
|
|
|
|
return (
|
|
<Modal open={isOpen()}>
|
|
<div class="hidden xl:block">
|
|
<ModalCloseButton onClick={() => setLocalState("showWelcome", false)} />
|
|
</div>
|
|
<div class="max-h-[50vh] overflow-y-auto px-2">
|
|
<div class="min-h-[50vh] flex items-center justify-center">
|
|
<div>
|
|
<div class="flex flex-col items-center justify-around">
|
|
<div class="mt-3 mb-3 text-6xl lg:text-8xl flex items-end lg:w-[370px] justify-between gap-2 lg:gap-0 font-bold tracking-tighter leading-none text-swiss-red fill-current">
|
|
<AppIcon class="w-auto h-[0.9em]" />
|
|
<div>Räppli</div>
|
|
</div>
|
|
<div class="flex flex-col items-stretch">
|
|
<div class="flex justify-center">
|
|
<div class="text-gray-800 text-sm lg:text-base tracking-widest mb-4 flex gap-1 flex-col items-center lg:flex-row">
|
|
Der reibungslose Weg zur{" "}
|
|
<span class="ignore-white-space" ref={subtitleEl}></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end lg:w-[410px] max-w-full">
|
|
<a
|
|
href="https://lufrai.org"
|
|
target="_blank"
|
|
rel="noopener"
|
|
class="text-lufrai-primary-darker hover:text-lufrai-primary transition-colors fill-current text-base flex items-center leading-tight gap-2 tracking-tighter font-bold border-b border-lufrai-primary-light border-opacity-20 pb-1"
|
|
>
|
|
<span>made</span>
|
|
<span>by</span>
|
|
<LufraiLogo class="w-auto h-[1.5em]" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-28 text-base flex flex-wrap text-center gap-7 items-center justify-center">
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary"
|
|
href="#welcome-quickstart"
|
|
>
|
|
Einleitung
|
|
</a>
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary"
|
|
href="#welcome-why-free"
|
|
>
|
|
Wieso ist Räppli <br />
|
|
<strong>komplett kostenlos</strong>?
|
|
</a>
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary"
|
|
href="#welcome-lufrai"
|
|
>
|
|
Was ist Lufrai?
|
|
</a>
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary flex items-center gap-2"
|
|
href="#welcome-opensource"
|
|
>
|
|
Open Source
|
|
</a>
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary"
|
|
href="#welcome-patron"
|
|
>
|
|
Das Projekt
|
|
<br />
|
|
unterstützen
|
|
</a>
|
|
<a
|
|
class="link text-secondary-focus hover:text-secondary"
|
|
title="Häufig gestellte Fragen"
|
|
href="#welcome-faq"
|
|
>
|
|
FAQ
|
|
</a>
|
|
</div>
|
|
|
|
<div class="mt-16 flex flex-wrap gap-3 items-center justify-center">
|
|
<div class="text-sm text-slate-600">Teile es auf:</div>
|
|
<div
|
|
class="shareon flex flex-wrap items-center justify-center"
|
|
data-title="Hast du schon von Räppli gehört? Es ist ein praktisches Web App, womit Schweizer QR Rechnungen inkl. Rechnungspositionen erstellt werden können und es ist komplett kostenlos! Probiere es aus unter:"
|
|
data-url={getHost()}
|
|
>
|
|
<ShareonLink provider="mastodon" />
|
|
<ShareonLink provider="telegram" />
|
|
<ShareonLink provider="reddit" />
|
|
<ShareonLink provider="odnoklassniki" />
|
|
<ShareonLink provider="pinterest" />
|
|
<ShareonLink provider="pocket" />
|
|
<ShareonLink provider="viber" />
|
|
<ShareonLink provider="vkontakte" />
|
|
<ShareonLink provider="linkedin" />
|
|
<ShareonLink provider="twitter" via="katy_wings" />
|
|
<ShareonLink provider="facebook" />
|
|
<ShareonLink provider="whatsapp" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-20 prose">
|
|
<section>
|
|
<h2 id="welcome-quickstart">Einleitung</h2>
|
|
<p>{description}</p>
|
|
</section>
|
|
|
|
<section class="mt-16">
|
|
<h2 id="welcome-why-free">
|
|
Wieso ist <span class="text-swiss-red">Räppli</span> komplett{" "}
|
|
<span class="text-swiss-red">kostenlos</span>?
|
|
</h2>
|
|
<p>
|
|
Consectetur id magna labore commodo exercitation laboris est
|
|
laboris consectetur irure minim. Officia anim tempor adipisicing
|
|
irure labore tempor reprehenderit culpa consequat ea esse
|
|
exercitation. Consectetur labore velit nulla excepteur eiusmod sit
|
|
fugiat do proident. Ex non consectetur mollit dolor dolore Lorem
|
|
ut et incididunt pariatur sunt deserunt tempor magna ullamco.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="mt-16">
|
|
<h2 id="welcome-lufrai">
|
|
Was ist <span class="text-lufrai-primary">Lufrai</span>?
|
|
</h2>
|
|
<p>
|
|
Quis occaecat pariatur laborum do ad esse. Mollit excepteur duis
|
|
nulla proident nostrud tempor ad ullamco. Amet id magna aute esse
|
|
tempor incididunt pariatur veniam ipsum qui. Sunt laboris in
|
|
laborum reprehenderit qui sint consectetur nostrud excepteur
|
|
proident proident laboris qui dolor ea. Reprehenderit tempor elit
|
|
consequat dolore quis ad voluptate consequat. Eiusmod ad quis
|
|
dolore dolore ea ipsum commodo eu aliquip veniam. Consequat
|
|
pariatur est do sint nisi duis enim tempor occaecat elit non.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="mt-16">
|
|
<h2 id="welcome-opensource">Open Source</h2>
|
|
<p>
|
|
Quis occaecat pariatur laborum do ad esse. Mollit excepteur duis
|
|
nulla proident nostrud tempor ad ullamco. Amet id magna aute esse
|
|
tempor incididunt pariatur veniam ipsum qui. Sunt laboris in
|
|
laborum reprehenderit qui sint consectetur nostrud excepteur
|
|
proident proident laboris qui dolor ea. Reprehenderit tempor elit
|
|
consequat dolore quis ad voluptate consequat. Eiusmod ad quis
|
|
dolore dolore ea ipsum commodo eu aliquip veniam. Consequat
|
|
pariatur est do sint nisi duis enim tempor occaecat elit non.
|
|
</p>
|
|
|
|
<a
|
|
class="btn btn-sm flex items-center gap-2"
|
|
target="_blank"
|
|
rel="noopener"
|
|
href="https://git.lufrai.com/rappli/rappli"
|
|
>
|
|
Git-Repository
|
|
<ExternalLinkIcon />
|
|
</a>
|
|
</section>
|
|
|
|
<section class="mt-16">
|
|
<h2 id="welcome-patron">Das Projekt unterstützen</h2>
|
|
<p>
|
|
Irure laboris quis consequat enim tempor dolor. Esse velit
|
|
occaecat dolore aute cillum pariatur reprehenderit irure duis eu
|
|
nulla pariatur fugiat consectetur ipsum. Commodo ad aliquip nulla
|
|
non incididunt. Officia veniam cillum cillum. Velit ex aliquip
|
|
mollit deserunt nostrud id amet voluptate ea duis cupidatat
|
|
officia culpa consequat enim. Voluptate anim fugiat anim et elit
|
|
aute cupidatat. Duis aliquip laboris adipisicing dolore elit
|
|
voluptate proident occaecat excepteur culpa exercitation velit.
|
|
Veniam esse quis voluptate aliquip culpa. Aute cupidatat sunt amet
|
|
ad fugiat id elit. Officia proident ea deserunt quis anim elit
|
|
cupidatat pariatur. Aliqua dolore ad eiusmod qui eu ea enim qui
|
|
enim incididunt aute irure tempor fugiat sunt. Consequat magna
|
|
culpa Lorem nostrud cillum eu cillum adipisicing eu aute duis
|
|
excepteur. In anim mollit amet elit ex.
|
|
</p>
|
|
<h3>Spende</h3>
|
|
<h3>Mitwirkung</h3>
|
|
</section>
|
|
|
|
<section class="mt-16">
|
|
<h2 id="welcome-faq">Häufig gestellte Fragen</h2>
|
|
<div class="text-black">
|
|
<AcordionItem
|
|
label={"1. Wo werden meine Daten gespeichert?"}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label="2. Ich habe vergessen zu speichern, was nun?!"
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={
|
|
<>
|
|
3. Ich habe einen Anpassungswunsch!
|
|
<br />
|
|
Kann die App bitte gratis angepasst werden?
|
|
</>
|
|
}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={
|
|
<div>
|
|
4. Wie können Aufwand-Schätzungen gemacht werden?
|
|
<br />
|
|
Was hat es mit <strong>Agile</strong> auf sich?
|
|
</div>
|
|
}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={"5. Welche Geräte werden unterstützt?"}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={
|
|
<div>
|
|
6. Können Fliesstexte formatiert werden?
|
|
<br />
|
|
Was ist <strong>Markdown</strong>?
|
|
</div>
|
|
}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={<div>7. Kann Räppli lokal installiert werden?</div>}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
|
|
<AcordionItem
|
|
label={
|
|
<div>
|
|
8. Wie präzise wurden die SIX QR-Rechnung Vorgaben
|
|
umgesetzt?
|
|
</div>
|
|
}
|
|
alignCenter={false}
|
|
>
|
|
test
|
|
</AcordionItem>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
<div class="mt-14 relative flex justify-center items-center gap-10">
|
|
<a
|
|
href="#welcome-patron"
|
|
class="hidden xl:flex btn btn-sm btn-secondary bg-purple-600 border-purple-600 gap-2 transition-all shadow-lg hover:shadow-lg shadow-purple-300 hover:shadow-purple-500 hover:ring-2 ring-offset-2 ring-purple-500"
|
|
>
|
|
<DonationIcon />
|
|
Spenden
|
|
</a>
|
|
<div
|
|
class="hidden xl:flex form-control flex-row items-center gap-2 tooltip tooltip-accent"
|
|
data-tip="Füge das Lufrai Logo zu deinem Dokument hinzu."
|
|
>
|
|
<input
|
|
id="welcome-modal-show-watermark"
|
|
checked={localState.showLufraiWatermark}
|
|
class="checkbox checkbox-lg checkbox-accent shadow-md"
|
|
type="checkbox"
|
|
onClick={(evt) =>
|
|
setLocalState("showLufraiWatermark", evt.currentTarget.checked)
|
|
}
|
|
/>
|
|
<label
|
|
for="welcome-modal-show-watermark"
|
|
class="btn btn-sm btn-accent gap-2 shadow-md"
|
|
>
|
|
<WatermarkIcon />
|
|
Für Lufrai werben
|
|
</label>
|
|
</div>
|
|
<button
|
|
onClick={() => setLocalState("showWelcome", false)}
|
|
class="hidden xl:flex btn btn-xl btn-primary gap-2 transition-all shadow-lg hover:shadow-lg shadow-indigo-300 hover:shadow-indigo-500 hover:ring-2 ring-offset-2 ring-indigo-500"
|
|
>
|
|
<LaunchIcon />
|
|
Loslegen
|
|
</button>
|
|
<div class="w-full text-sm xl:hidden bg-error bg-opacity-50 ring-2 ring-offset-2 ring-error text-error-content rounded p-3 shadow-2xl flex items-center justify-center gap-3">
|
|
<WarningIcon /> Bitte verwende einen Laptop oder Computer!
|
|
</div>
|
|
</div>
|
|
<div class="absolute bottom-8 right-8 text-xs text-black opacity-60 flex gap-3">
|
|
<span>Version: {__APP_VERSION__}</span>
|
|
<span>{getDisplayDate(new Date(__BUILD_TIME__))}</span>
|
|
</div>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default WelcomeModal;
|