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 >
< h1 id = "welcome-quickstart" > Einleitung < / h1 >
< p > { description } < / p >
< / section >
< section class = "mt-16" >
< h1 id = "welcome-why-free" >
Wieso ist < span class = "text-swiss-red" > R ä ppli < / span > komplett { " " }
< span class = "text-swiss-red" > kostenlos < / span > ?
< / h1 >
< 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" >
< h1 id = "welcome-lufrai" >
Was ist < span class = "text-lufrai-primary" > Lufrai < / span > ?
< / h1 >
< 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" >
< h1 id = "welcome-opensource" > Open Source < / h1 >
< 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" >
< h1 id = "welcome-patron" > Das Projekt unterst ü tzen < / h1 >
< 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 >
< h2 > Spende < / h2 >
< h2 > Mitwirkung < / h2 >
< / section >
< section class = "mt-16" >
< h1 id = "welcome-faq" > H ä ufig gestellte Fragen < / h1 >
< 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 ;