start component tree + implem navbar
This commit is contained in:
Generated
+1617
File diff suppressed because it is too large
Load Diff
+9
-1
@@ -8,9 +8,13 @@
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --fix",
|
||||
"format": "prettier --write ."
|
||||
"format": "prettier --write .",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@supabase/supabase-js": "^2.112.3",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"pinia": "^4.0.3",
|
||||
@@ -22,16 +26,20 @@
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^24.13.3",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@vitest/ui": "^4.1.11",
|
||||
"@vue/eslint-config-typescript": "^14.9.0",
|
||||
"@vue/test-utils": "^2.4.11",
|
||||
"@vue/tsconfig": "^0.9.1",
|
||||
"eslint": "^10.8.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-vue": "^10.10.0",
|
||||
"jsdom": "^30.0.1",
|
||||
"prettier": "^3.9.6",
|
||||
"typescript": "~6.0.2",
|
||||
"typescript-eslint": "^8.67.0",
|
||||
"vite": "^8.2.0",
|
||||
"vitest": "^4.1.11",
|
||||
"vue-tsc": "^3.3.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import EntryItem from './EntryItem.vue'
|
||||
|
||||
describe('EntryItem', () => {
|
||||
it('renders the label and formatted amount', () => {
|
||||
const wrapper = mount(EntryItem, {
|
||||
props: { label: 'Rent', amount: 850 },
|
||||
})
|
||||
expect(wrapper.text()).toContain('Rent')
|
||||
expect(wrapper.text()).toContain('850')
|
||||
})
|
||||
|
||||
it('emits "remove" with the entry id when delete is clicked', async () => {
|
||||
const wrapper = mount(EntryItem, {
|
||||
props: { id: 'random_id', label: 'Rent', amount: 850 },
|
||||
})
|
||||
await wrapper.find('[data-test="delete-btn"]').trigger('click')
|
||||
expect(wrapper.emitted('remove')).toEqual([['random_id']])
|
||||
})
|
||||
|
||||
it('matches snapshot', () => {
|
||||
const wrapper = mount(EntryItem, {
|
||||
props: { label: 'Rent', amount: 850 },
|
||||
})
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,2 @@
|
||||
<template></template>
|
||||
<script setup lang="ts"></script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<Disclosure as="nav" class="rounded bg-tints-0 dark:bg-shades-100" v-slot="{ open }">
|
||||
<div class="flex h-16 items-center justify-between px-4">
|
||||
<div class="flex items-center gap-6">
|
||||
<span class="text-lg font-semibold text-shades-200 dark:text-shades-900">Budget</span>
|
||||
<div class="hidden sm:flex sm:gap-2">
|
||||
<a
|
||||
v-for="item in navigation"
|
||||
:key="item.name"
|
||||
:href="item.href"
|
||||
:class="[
|
||||
item.current
|
||||
? 'bg-shades-500 text-tints-0 dark:bg-tints-1000 dark:text-shades-100'
|
||||
: 'text-shades-300 hover:bg-shades-800 dark:text-shades-700 dark:hover:bg-shades-300/20',
|
||||
'rounded-md px-3 py-2 text-sm font-medium',
|
||||
]"
|
||||
:aria-current="item.current ? 'page' : undefined"
|
||||
>{{ item.name }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md p-2 text-shades-300 hover:bg-shades-800 dark:text-shades-700 dark:hover:bg-shades-300/20"
|
||||
@click="toggle"
|
||||
>
|
||||
<span class="sr-only">Toggle dark mode</span>
|
||||
<SunIcon v-if="isDark" class="size-5" aria-hidden="true" />
|
||||
<MoonIcon v-else class="size-5" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<DisclosureButton
|
||||
class="rounded-md p-2 text-shades-300 hover:bg-shades-800 sm:hidden dark:text-shades-700 dark:hover:bg-shades-300/20"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<Bars3Icon v-if="!open" class="size-6" aria-hidden="true" />
|
||||
<XMarkIcon v-else class="size-6" aria-hidden="true" />
|
||||
</DisclosureButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DisclosurePanel class="space-y-1 px-4 pb-3 sm:hidden">
|
||||
<DisclosureButton
|
||||
v-for="item in navigation"
|
||||
:key="item.name"
|
||||
as="a"
|
||||
:href="item.href"
|
||||
:class="[
|
||||
item.current
|
||||
? 'bg-shades-500 text-tints-0 dark:bg-tints-1000 dark:text-shades-100'
|
||||
: 'text-shades-300 hover:bg-shades-800 dark:text-shades-700 dark:hover:bg-shades-300/20',
|
||||
'block rounded-md px-3 py-2 text-base font-medium',
|
||||
]"
|
||||
:aria-current="item.current ? 'page' : undefined"
|
||||
>{{ item.name }}</DisclosureButton
|
||||
>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDarkMode } from '@/composables/useDarkMode'
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
||||
import { Bars3Icon, MoonIcon, SunIcon, XMarkIcon } from '@heroicons/vue/24/outline'
|
||||
|
||||
const { isDark, toggle } = useDarkMode()
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Dashboard', href: '#', current: true },
|
||||
{ name: 'History', href: '#', current: false },
|
||||
]
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="h-16 flex items-center justify-start bg-tints-0 dark:bg-shades-100">
|
||||
<span>Amount</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
<template>Spend Columns</template>
|
||||
@@ -0,0 +1 @@
|
||||
<template>Spend Input</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
const isDark = ref(false)
|
||||
|
||||
function apply() {
|
||||
document.documentElement.classList.toggle('dark', isDark.value)
|
||||
}
|
||||
|
||||
function init() {
|
||||
const stored = localStorage.getItem('theme')
|
||||
isDark.value = stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
apply()
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
isDark.value = !isDark.value
|
||||
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
|
||||
apply()
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
export function useDarkMode() {
|
||||
return { isDark, toggle }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import Navbar from '@/components/Navbar/Navbar.vue'
|
||||
import RemainingAmount from '@/components/RemainingAmount/RemainingAmount.vue'
|
||||
import SpendColumns from '@/components/SpendColumns/SpendColumns.vue'
|
||||
import SpendInput from '@/components/SpendInput/SpendInput.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container flex flex-col space-x-1 space-y-2 p-2">
|
||||
<div class="w-full rounded"><Navbar /></div>
|
||||
<div class="h-12 w-full rounded"><RemainingAmount /></div>
|
||||
<div class="h-128 w-full rounded"><SpendColumns /></div>
|
||||
<div class="h-16 w-full rounded"><SpendInput /></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<template></template>
|
||||
|
||||
+24
-1
@@ -1 +1,24 @@
|
||||
@import "tailwindcss";
|
||||
@import "tailwindcss";
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--color-shades-900: #f8ead3;
|
||||
--color-shades-800: #f2d6a6;
|
||||
--color-shades-700: #edc278;
|
||||
--color-shades-600: #eaaf48;
|
||||
--color-shades-500: #e89b17;
|
||||
--color-shades-400: #bc7d10;
|
||||
--color-shades-300: #8f5e0a;
|
||||
--color-shades-200: #613f05;
|
||||
--color-shades-100: #312002;
|
||||
--color-tints-1000: #f59f0a;
|
||||
--color-tints-880: #f2a92c;
|
||||
--color-tints-750: #f0b248;
|
||||
--color-tints-630: #efbd67;
|
||||
--color-tints-500: #efc885;
|
||||
--color-tints-380: #f0d29e;
|
||||
--color-tints-250: #f3deba;
|
||||
--color-tints-130: #f5e8d1;
|
||||
--color-tints-0: #faf4ea;
|
||||
}
|
||||
+5
-1
@@ -1,11 +1,15 @@
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import { defineConfig } from 'vite'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), tailwindcss()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
|
||||
Reference in New Issue
Block a user