Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
prokawsar committed Jul 15, 2024
2 parents 4d26c17 + 6a2dfb7 commit dbba53b
Show file tree
Hide file tree
Showing 20 changed files with 402 additions and 141 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"type": "module",
"dependencies": {
"@fontsource/jost": "^5.0.18",
"@supabase/supabase-js": "^2.44.3",
"dayjs": "^1.11.11",
"mixpanel-browser": "^2.53.0",
"svelte-persisted-store": "^0.11.0",
Expand Down
7 changes: 6 additions & 1 deletion src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
<link rel="manifest" crossorigin="use-credentials" href="manifest.json" hreflang="en" />
<link
rel="manifest"
crossorigin="use-credentials"
href="%sveltekit.assets%/manifest.json"
hreflang="en"
/>

%sveltekit.head%
</head>
Expand Down
4 changes: 4 additions & 0 deletions src/lib/db/supabaseClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PUBLIC_SUPABASE_ANON, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON)
13 changes: 13 additions & 0 deletions src/lib/elements/Button.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
<script lang="ts">
import { onMount } from 'svelte'
export let disabled = false
export let text = ''
export let classNames = ''
export let type: 'button' | 'submit' | 'reset' = 'button'
let buttonRef: HTMLButtonElement
onMount(() => {
if (buttonRef) {
buttonRef.type = type
}
})
</script>

<button
bind:this={buttonRef}
{disabled}
{type}
class="border border-gray-400 rounded-md text-teal-600 font-semibold px-3 py-1 w-fit disabled:text-opacity-60 {classNames}"
on:click
>
Expand Down
17 changes: 12 additions & 5 deletions src/lib/elements/Input.svelte
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
<script lang="ts">
import { focusedInputStore } from '$lib/stores'
import { makeid } from '$lib/utils/tools'
import { onMount } from 'svelte'
export let id: string = makeid(3)
export let value: string
export let placeholder: string = ''
export let type: string = 'number'
export let disabled: boolean = false
export let classNames: string = ''
let inputRef: HTMLInputElement
onMount(() => {
if (inputRef) {
inputRef.type = type
}
})
</script>

<input
bind:this={inputRef}
{id}
{disabled}
class="input-field focus:!border-[1.5px] focus:!border-teal-500 focus:outline-none"
type="number"
{placeholder}
class="input-field focus:!border-[1.5px] focus:!border-teal-500 focus:outline-none {classNames}"
bind:value
on:keydown
on:focus
on:focus={() => ($focusedInputStore = inputRef)}
{...$$restProps}
/>

<style lang="postcss">
.input-field {
@apply border border-gray-400 w-14 p-1 rounded;
@apply border border-gray-400 w-12 p-1 rounded;
}
</style>
6 changes: 6 additions & 0 deletions src/lib/elements/Loader.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script lang="ts">
import Icon from '@iconify/svelte'
</script>

<!-- <Icon icon="svg-spinners:wind-toy" width="36px" /> -->
<Icon icon="ei:spinner-3" width="44px" class="animate-spin" />
14 changes: 1 addition & 13 deletions src/lib/stores/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
import type { Paper } from '$lib/utils/services'
import { persisted } from 'svelte-persisted-store'
import { writable, type Writable } from 'svelte/store'

export type PaperHistory = {
id: string
finalPrice: number
date?: Date
papers: Paper[]
}
function getPaperStore(): Writable<{ history: PaperHistory[] }> {
return persisted<{ history: PaperHistory[] }>('paper_cost_history', { history: [] })
}

export const paperHistoryStore = getPaperStore()
export const focusedInputStore: Writable<HTMLInputElement | null> = writable(null)
export const totalHistoryStore: Writable<number> = writable(0)
50 changes: 50 additions & 0 deletions src/lib/utils/services.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { supabase } from '$lib/db/supabaseClient'

export type Paper = {
id: string
length: string
Expand All @@ -7,5 +9,53 @@ export type Paper = {
[key: string]: string
}

export type CostHistoryType = {
id?: string
name: string
papers: Paper[]
final_price: number
created_at?: string
}

export const PAPER_FIXED = 1550000
export const MAX_PAPER = 10
export const MAX_HISTORY = 20

export const calculateCost = (paper: Paper): number => {
const paperSize = parseFloat(paper.length) * parseFloat(paper.width) * parseFloat(paper.thickness)
const result = paperSize / PAPER_FIXED
return result * parseFloat(paper.rate)
}

export const addHistory = async (history: CostHistoryType) => {
const { data, error } = await supabase.from('history').insert({
name: history.name || '',
papers: history.papers || [],
final_price: history.final_price || 100
})
if (!error) {
return data
}
return error
}

export const getTotalHistory = async (): Promise<number> => {
const { count } = await await supabase.from('history').select('*', { count: 'exact' })
if (count) {
return count
}
return 0
}

export const getHistory = async (id: string): Promise<CostHistoryType[] | null> => {
const { data, error } = await supabase.from('history').select().eq('id', id)
if (!error) {
return data as unknown as CostHistoryType[]
}
return null
}

export const deleteHistory = async (id: string) => {
const response = await supabase.from('history').delete().eq('id', id)
return response
}
49 changes: 49 additions & 0 deletions src/routes/(app)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts">
import { page } from '$app/stores'
import { PUBLIC_MIX_TOKEN } from '$env/static/public'
import '$lib/app.css'
import '@fontsource/jost'
import { navigating } from '$app/stores'
import Loader from '$lib/elements/Loader.svelte'
import mixpanel from 'mixpanel-browser'
//Import Mixpanel SDK
mixpanel.init(PUBLIC_MIX_TOKEN, {
debug: false,
track_pageview: true,
persistence: 'localStorage'
})
</script>

<main class="h-[100svh] flex flex-col">
{#if $navigating}
<div
class="absolute bg-white bg-opacity-80 flex h-full w-full items-center justify-center z-10"
>
<Loader />
</div>
{/if}
<nav>
<div class="flex justify-center py-2">
<p class="text-center text-2xl text-red-600 font-semibold">Molla Printing & Packaging</p>
</div>
<div
class="w-full max-w-6xl mx-auto bg-gradient-to-r from-transparent via-orange-800/40 to-transparent p-[1px]"
/>
<div class="px-4 flex w-full mt-2">
<div class="flex w-full flex-row justify-end gap-3 py-1 px-2 rounded border border-teal-400">
<a href="/history" class:hidden={$page.url.pathname == '/history'} class="h-full">History</a
>
<a href="/" class:!block={$page.url.pathname == '/history'} class="hidden h-full">Home</a>
</div>
</div>
</nav>
<slot />

<div class="absolute bottom-0 w-full">
<p class="text-center text-gray-400">
&#x1F4BB;Developed by <a href="https://github.com/prokawsar" target="_blank">ProKawsar</a
>&#x1F60E;
</p>
</div>
</main>
62 changes: 44 additions & 18 deletions src/routes/+page.svelte → src/routes/(app)/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
import Button from '$lib/elements/Button.svelte'
import Input from '$lib/elements/Input.svelte'
import Result from '$lib/elements/Result.svelte'
import { focusedInputStore, paperHistoryStore } from '$lib/stores'
import { MAX_PAPER, PAPER_FIXED, type Paper } from '$lib/utils/services'
import { focusedInputStore, totalHistoryStore } from '$lib/stores'
import {
addHistory,
calculateCost,
getTotalHistory,
MAX_HISTORY,
MAX_PAPER,
type Paper
} from '$lib/utils/services'
import { makeid } from '$lib/utils/tools'
import Icon from '@iconify/svelte'
import mixpanel from 'mixpanel-browser'
Expand All @@ -30,6 +37,7 @@
let inputs: NodeListOf<HTMLInputElement> | null
let inputGroupRef: HTMLDivElement
let focusedIndex = 0
let customer_name = ''
const addPaper = async () => {
paperCount.push({ ...paperFields, id: makeid(5) })
Expand All @@ -42,29 +50,27 @@
getAllInputs()
}
const calculatePaperCost = () => {
const calculatePaperCost = async () => {
if (!paperCount.length || hasNullValue) return
perPaperResult.clear()
finalPrice = 0
paperCount.forEach((paper) => {
const paperSize =
parseFloat(paper.length) * parseFloat(paper.width) * parseFloat(paper.thickness)
const result = paperSize / PAPER_FIXED
const totalPerPaper = result * parseFloat(paper.rate)
const totalPerPaper = calculateCost(paper)
perPaperResult.set(paper.id, totalPerPaper)
finalPrice += totalPerPaper
})
perPaperResult = perPaperResult
// Saving to history
$paperHistoryStore.history.push({
id: makeid(6),
finalPrice,
date: new Date(),
papers: paperCount
})
if ($totalHistoryStore < MAX_HISTORY) {
addHistory({
name: customer_name,
final_price: finalPrice,
papers: paperCount
})
$totalHistoryStore = await getTotalHistory()
}
// mixpanel data prepare
const perPageData: number[] = []
perPaperResult.forEach((data) => {
Expand Down Expand Up @@ -115,29 +121,49 @@
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter' && inputs) {
focusedIndex++
event.preventDefault()
focusedIndex++
focusedIndex = Math.min(focusedIndex, inputs.length)
// Calculate on final input field
if (focusedIndex == inputs.length) {
calculatePaperCost()
return
}
const nextInput = inputs[focusedIndex]
if (nextInput) {
setFocus(nextInput)
}
}
}
onMount(() => {
onMount(async () => {
inputs = inputGroupRef.querySelectorAll('input')
setFocus()
$totalHistoryStore = await getTotalHistory()
})
</script>

<svelte:head>
<title>Paper Cost Calculator</title>
</svelte:head>

<section class="max-w-6xl mx-auto flex w-full max-h-[85%] flex-col gap-4 px-4 py-5">
<section class="max-w-6xl mx-auto flex w-full max-h-[85%] flex-col gap-3 px-4 py-3">
<h1 class="text-xl text-center text-teal-500 font-semibold">Paper Cost</h1>
<div class="w-full bg-gradient-to-r from-transparent via-slate-600/10 to-transparent p-[1px]" />
<div class="flex flex-col w-full justify-between gap-4 h-[90%] items-center">
<div class="flex flex-col w-full justify-between gap-2 h-[90%] items-center">
{#if $totalHistoryStore >= MAX_HISTORY}
<p class="text-sm text-yellow-600 animate-pulse">
Maximum history reached, delete some history!
</p>
{/if}
<div class="flex w-full items-start">
<input
bind:value={customer_name}
type="text"
placeholder="Customer name"
class="border-b border-dashed w-full px-2 focus:outline-none focus:border-teal-500"
/>
</div>
<div
class="flex flex-col gap-[1px] overflow-y-auto max-w-3xl max-h-[85%] py-2 w-full"
bind:this={inputGroupRef}
Expand Down
8 changes: 8 additions & 0 deletions src/routes/(app)/history/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { supabase } from '$lib/db/supabaseClient'

export async function load() {
const { data } = await supabase.from('history').select()
return {
histories: data ?? []
}
}
Loading

0 comments on commit dbba53b

Please sign in to comment.