[FWA-0] Update Header Style

This commit is contained in:
2024-06-19 04:57:09 +00:00
parent e6b56e3b40
commit e6923277ed
27 changed files with 1004 additions and 55 deletions

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 166 155.3"><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" fill="#76b3e1"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.5" y1="3" x2="152" y2="63.5"><stop offset=".1" stop-color="#76b3e1"/><stop offset=".3" stop-color="#dcf2fd"/><stop offset="1" stop-color="#76b3e1"/></linearGradient><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" opacity=".3" fill="url(#a)"/><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" fill="#518ac8"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.8" y1="32.6" x2="74" y2="105.2"><stop offset="0" stop-color="#76b3e1"/><stop offset=".5" stop-color="#4377bb"/><stop offset="1" stop-color="#1f3b77"/></linearGradient><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" opacity=".3" fill="url(#b)"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.4" y1="64.2" x2="144.3" y2="149.8"><stop offset="0" stop-color="#315aa9"/><stop offset=".5" stop-color="#518ac8"/><stop offset="1" stop-color="#315aa9"/></linearGradient><path d="M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z" fill="url(#c)"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.2" y1="74.5" x2="24.4" y2="260.8"><stop offset="0" stop-color="#4377bb"/><stop offset=".5" stop-color="#1a336b"/><stop offset="1" stop-color="#1a336b"/></linearGradient><path d="M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z" fill="url(#d)"/></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -4,7 +4,7 @@ import { useSiteContext } from '@context/SiteContext'
import useAuth from '@hooks/useAuth'
import useToast from '@hooks/useToast'
import { A } from '@solidjs/router'
import { IconLogout, IconMenuDeep } from '@tabler/icons-solidjs'
import { IconLogout, IconMenuDeep, IconUserCircle } from '@tabler/icons-solidjs'
import { Show, onMount } from 'solid-js'
export default function Header() {
@ -40,34 +40,51 @@ export default function Header() {
}
return (
<header class="w-full navbar py-3 px-4 items-center justify-between bg-emerald-500">
<div class="flex items-center justify-end">
<header class="w-full navbar py-3 px-4 items-center justify-between bg-fu-primary">
<div class="flex-1">
<A href="/" class="text-white flex items-center hover:text-white">
<img src={Logo} class="w-8" alt="Logo" />
<span class="ml-2 text-2xl">Fuware</span>
</A>
</div>
<Show when={store.auth}>
<div class="flex items-center justify-end">
<div class="avatar">
<div class="w-9 rounded-full">
<A href="/me" class="hidden lg:block">
<div class="flex-initial">
<div class="dropdown dropdown-end hidden lg:flex">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-circle w-10 h-10 min-h-10 avatar"
>
<div class="w-8 rounded-full">
<img
src={`https://ui-avatars.com/api/?name=${store.userInfo?.name}`}
src={`https://ui-avatars.com/api/?name=${store.userInfo?.name}&background=fb9678`}
alt="avatar"
/>
</A>
</div>
</div>
<ul
tabindex="0"
class="mt-12 z-[1] p-2 shadow-md menu menu-sm dropdown-content bg-base-100 rounded-box w-52"
>
<li class="border rounded-full mb-1">
<span class="menu-title text-black font-bold !py-1">
{store.userInfo?.name}
</span>
</li>
<li class="mb-1">
<A href="/me">
<IconUserCircle size={15} />
Profile
</A>
</li>
<li>
<a onClick={logOut}>
<IconLogout size={15} />
Logout
</a>
</li>
</ul>
</div>
<A
href="/me"
class="mx-3 text-white hover:text-white hidden lg:block"
>
{store.userInfo?.name}
</A>
<button class="btn btn-ghost btn-sm hidden lg:block" onClick={logOut}>
<IconLogout size={16} />
</button>
<label
for="nav-menu"
class="btn btn-ghost btn-sm drawer-button pr-0 lg:hidden"

View File

@ -5,8 +5,8 @@ import { A } from '@solidjs/router'
import {
IconBuildingWarehouse,
IconDashboard,
IconLogout,
IconTriangle
IconHome,
IconTriangle,
} from '@tabler/icons-solidjs'
import { For, Show, mergeProps } from 'solid-js'
import { Dynamic } from 'solid-js/web'
@ -21,6 +21,12 @@ export const NAV_ITEM = (admin = false) => [
icon: IconDashboard,
text: language?.ui.dashboard,
},
{
path: '/house',
show: true,
icon: IconHome,
text: language?.ui.house,
},
{
path: '/warehouse',
show: true,
@ -94,29 +100,48 @@ export default function Navbar() {
return (
<div class="drawer-side lg:h-[calc(100svh-64px)]">
<label for="nav-menu" aria-label="close sidebar" class="drawer-overlay" />
<div class="bg-base-200 w-80 min-h-full">
<div class="bg-base-200 w-80 max-w-[90vw] min-h-full">
<Show when={store.auth}>
<div class="flex items-center justify-between px-3 pt-5 lg:hidden">
<div class="avatar">
<div class="w-9 mask mask-hexagon">
<img
src={`https://ui-avatars.com/api/?name=${store.userInfo?.name}`}
alt="avatar"
/>
<div class="flex items-center px-3 pt-3 lg:hidden">
<div class="dropdown dropdown-start flex mr-2">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-circle w-10 h-10 min-h-10 avatar"
>
<div class="w-8 rounded-full">
<img
src={`https://ui-avatars.com/api/?name=${store.userInfo?.name}&background=fb9678`}
alt="avatar"
/>
</div>
</div>
<ul
tabindex="0"
class="mt-12 z-[1] p-2 shadow-md menu menu-sm dropdown-content bg-base-100 rounded-box w-52"
>
<li class="border rounded-full mb-1">
<span class="menu-title text-black font-bold !py-1">
{store.userInfo?.name}
</span>
</li>
<li class="mb-1">
<A href="/me">Profile</A>
</li>
<li>
<a onClick={logOut}>Logout</a>
</li>
</ul>
</div>
<span class="mx-3 line-clamp-1">
<A href="/me">{store.userInfo?.name}</A>
<span class="text-black font-bold text-xl">
{store.userInfo?.name}
</span>
<button class="btn btn-ghost btn-sm" onClick={logOut}>
<IconLogout size={16} />
</button>
</div>
<div class="divider divider-success mb-0 lg:hidden">
<div class="divider divider-success mb-0 mt-2 lg:hidden">
<IconTriangle size={30} />
</div>
</Show>
<ul class="menu w-80 text-base-content pt-3 px-3 pb-6">
<ul class="menu w-full text-base-content pt-3 px-3 pb-6">
<For each={NAV_ITEM(store?.userInfo?.isAdmin)}>
{(item) => {
if (!item.show) return

View File

@ -0,0 +1,40 @@
import { IconGridDots, IconLayoutList } from '@tabler/icons-solidjs'
import { createSignal } from 'solid-js'
export const VIEWDATA = Object.freeze(
new Proxy(
{ list: 'list', grid: 'grid' },
{
get: (target, prop) => target[prop] ?? 'list',
},
),
)
export default function ViewSwitch(props) {
const [view, setView] = createSignal(VIEWDATA['list'])
const selectView = (view, event) => {
event.preventDefault()
setView(view)
props.switchView && props.switchView(view)
}
return (
<div class="view-switch join">
<button
class="btn join-item btn-sm"
classList={{ 'btn-primary': view() === VIEWDATA['list'] }}
onClick={[selectView, VIEWDATA['list']]}
>
<IconLayoutList size={15} />
</button>
<button
class="btn join-item btn-sm"
classList={{ 'btn-primary': view() === VIEWDATA['grid'] }}
onClick={[selectView, VIEWDATA['grid']]}
>
<IconGridDots size={15} />
</button>
</div>
)
}

View File

@ -0,0 +1,2 @@
export * from './ViewSwitch'
export { default } from './ViewSwitch'

View File

@ -11,20 +11,29 @@ export default function Finput(props) {
mode="input"
render={(field) => (
<div class="form-control w-full [&:not(:last-child)]:pb-3">
<Show when={local.label}>
<Show when={local.label && !props.line}>
<div class="label">
<span class="label-text">{local.label}</span>
</div>
</Show>
<label
class="input input-bordered flex items-center gap-2 w-full"
classList={{ 'input-error': field.helpers.error }}
>
<div class="join">
<Show when={local.icon}>
<Dynamic component={local.icon} size={18} />
<div
class={`join-item flex items-center bg-base-200 px-3 border border-gray-300 w-auto ${props.labelClass ? props.labelClass : ''}`}
>
<Dynamic component={local.icon} size={18} />
<Show when={local.label && !!props.line}>
<span class="ml-2 hidden md:block">{local.label}</span>
</Show>
</div>
</Show>
<input {...rest} class="grow w-full" {...field.props} />
</label>
<label
class="input input-bordered flex items-center gap-2 w-full join-item"
classList={{ 'input-error': field.helpers.error }}
>
<input {...rest} class="grow w-full" {...field.props} />
</label>
</div>
<Show when={field.helpers.error}>
<div class="label">
<span class="label-text-alt text-red-600">

View File

@ -9,10 +9,27 @@
"changeInfo": "Thông tin tài khoản",
"save": "Lưu",
"clear": "Xóa",
"house": "Nhà",
"action": "Thao Tác",
"createHouse": "Tạo mới nhà",
"location": "Nhà kho",
"displayName": "Display Name",
"newPassword": "New Password",
"confirmNewPassword": "Confirm New Password"
"displayName": "Tên hiển thị",
"newPassword": "Mật khẩu mới",
"confirmNewPassword": "Nhập lại mật khẩu",
"newHouse": "Tạo nhà mới",
"houseName": "Tên nhà",
"houseIcon": "Ký tự nhà",
"houseAddress": "Địa chỉ nhà",
"create": "Tạo"
},
"table": {
"columnName": {
"no": "STT",
"name": "Tên",
"icon": "Ký tự",
"address": "Địa chỉ",
"action": "Thao Tác"
}
},
"message": {
"CREATED_USER": "Username already registered!",

View File

@ -0,0 +1,85 @@
import ViewSwitch, { VIEWDATA } from '@components/ViewSwitch'
import useLanguage from '@hooks/useLanguage'
import { IconHome } from '@tabler/icons-solidjs'
import { createSignal } from 'solid-js'
import { A } from '@solidjs/router'
import {
IconHome2,
IconHomeDot,
IconPencil,
IconTrash,
} from '@tabler/icons-solidjs'
import './house.scss'
export default function House() {
const language = useLanguage()
const [view, setView] = createSignal(VIEWDATA['list'])
return (
<div class="house">
<div class="flex items-center gap-2 mb-5 text-xl">
<span class="text-secondary">
<IconHome size={30} />
</span>
{language.ui.house}
</div>
<div class="page-topbar flex justify-between mb-4">
<ViewSwitch switchView={setView} />
<A
href="/house/create"
class="btn btn-success text-white hover:text-white btn-sm"
>
{language.ui.createHouse}
</A>
</div>
<div
class="view-layout"
classList={{
'view-list': view() === VIEWDATA['list'],
'view-grid': view() === VIEWDATA['grid'],
}}
>
<div class="row view-head">
<div class="col w-1/12">{language.table.columnName.no}</div>
<div class="col w-1/12">{language.table.columnName.icon}</div>
<div class="col w-4/12">{language.table.columnName.name}</div>
<div class="col w-4/12">{language.table.columnName.address}</div>
<div class="col w-2/12">{language.table.columnName.action}</div>
</div>
<div class="row view-item">
<div class="col hide w-1/12">1</div>
<div class="col w-1/12">
<IconHome2 size={21} />
</div>
<div class="col w-4/12">Nhà 1</div>
<div class="col w-4/12">Data 4</div>
<div class="col actionbar w-2/12">
<button class="btn btn-ghost btn-sm px-1 text-blue-500 mr-2">
<IconPencil size={20} />
</button>
<button class="btn btn-ghost btn-sm px-1 text-red-500">
<IconTrash size={20} />
</button>
</div>
</div>
<div class="row view-item">
<div class="col hide w-1/12">2</div>
<div class="col w-1/12">
<IconHomeDot size={21} />
</div>
<div class="col w-4/12">Nhà 2</div>
<div class="col w-4/12">Data 4</div>
<div class="col actionbar w-2/12">
<button class="btn btn-ghost btn-sm px-1 text-blue-500 mr-2">
<IconPencil size={20} />
</button>
<button class="btn btn-ghost btn-sm px-1 text-red-500">
<IconTrash size={20} />
</button>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,44 @@
.view-layout {
&.view-list {
@apply flex flex-col rounded-2xl border shadow-md overflow-hidden;
& > .row {
@apply flex justify-between items-center flex-nowrap px-3 border-b border-gray-200 h-14;
&.view-head {
@apply bg-base-200 font-bold;
}
}
}
&.view-grid {
@apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5 mt-5;
& > .row {
&.view-item {
@apply card bg-base-100 shadow-md border border-gray-200 p-3 relative pb-8;
& > .col {
@apply w-auto
}
& > .hide {
@apply hidden;
visibility: hidden;
}
& > .actionbar {
@apply absolute right-0 bottom-0 flex px-3 py-1 w-auto invisible;
}
&:hover > .actionbar {
@apply visible;
}
}
&.view-head {
display: none;
visibility: hidden;
}
}
}
}

View File

@ -0,0 +1 @@
export { default } from './House'

View File

@ -0,0 +1,90 @@
import TextInput from '@components/common/TextInput'
import useLanguage from '@hooks/useLanguage'
import { A } from '@solidjs/router'
import {
IconAddressBook,
IconHome,
IconIcons,
IconTag,
} from '@tabler/icons-solidjs'
import { useFormHandler } from 'solid-form-handler'
import { yupSchema } from 'solid-form-handler/yup'
import * as yup from 'yup'
const houseSchema = yup.object({
name: yup.string().required('Name is required'),
password: yup.string().nullable().optional(),
'confirm-password': yup.string().when('password', {
is: (val) => !!(val && val.length > 0),
then: (schema) =>
schema.oneOf([yup.ref('password'), null], 'Passwords must match'),
}),
})
export default function HouseCreate() {
const language = useLanguage()
const formHandler = useFormHandler(yupSchema(houseSchema))
const { formData } = formHandler
const submit = async (event) => {
event.preventDefault()
await formHandler.validateForm()
}
return (
<div class="house-create">
<div class="text-sm breadcrumbs mb-2">
<ul>
<li>
<A href="/house">{language.ui.house}</A>
</li>
<li>{language.ui.newHouse}</li>
</ul>
</div>
<div class="flex items-center gap-2 mb-5 text-xl">
<span class="text-secondary">
<IconHome size={30} />
</span>
{language.ui.newHouse}
</div>
<div class="card w-full bg-base-100 shadow-lg border border-gray-200">
<div class="card-body">
<form autoComplete="off" onSubmit={submit}>
<TextInput
icon={IconIcons}
name="icon"
label={language.ui.houseIcon}
line
labelClass="md:w-40"
placeholder={language.ui.houseIcon}
formHandler={formHandler}
/>
<TextInput
icon={IconTag}
name="name"
label={language.ui.houseName}
line
labelClass="md:w-40"
placeholder={language.ui.houseName}
formHandler={formHandler}
/>
<TextInput
icon={IconAddressBook}
name="address"
label={language.ui.houseAddress}
line
labelClass="md:w-40"
placeholder={language.ui.houseAddress}
formHandler={formHandler}
/>
<div class="card-actions">
<button type="submit" class="btn btn-primary">
{language.ui.create}
</button>
</div>
</form>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,2 @@
export * from './HouseCreate'
export { default } from './HouseCreate'

View File

@ -70,7 +70,7 @@ export default function Profile() {
return (
<div class="profile">
<div class="divider divider-accent text-xl">
<div class="flex items-center gap-2 mb-5 text-xl">
<span class="text-secondary">
<IconUserCircle size={30} />
</span>
@ -84,6 +84,9 @@ export default function Profile() {
<TextInput
icon={IconUser}
name="name"
label={language.ui.displayName}
line
labelClass="md:w-56"
placeholder={language.ui.displayName}
formHandler={formHandler}
/>
@ -91,6 +94,9 @@ export default function Profile() {
icon={IconKey}
name="password"
type="password"
label={language.ui.newPassword}
line
labelClass="md:w-56"
placeholder={language.ui.newPassword}
formHandler={formHandler}
/>
@ -98,6 +104,9 @@ export default function Profile() {
icon={IconKey}
name="confirm-password"
type="password"
label={language.ui.confirmNewPassword}
line
labelClass="md:w-56"
placeholder={language.ui.confirmNewPassword}
formHandler={formHandler}
/>

View File

@ -7,14 +7,23 @@ function PackageItem(props) {
const { item } = { ...props }
const isEmpty = item % 2 === 0
return (
<a href="#" class={`group/item w-10 h-10 m-[5px] hover:text-white bg-fu-warning${isEmpty ? '/30' : ''} hover:bg-fu-green rounded-[10px] block`}>
<a
href="#"
class="group/item w-10 h-10 m-[5px] hover:text-white hover:bg-fu-green rounded-[10px] block"
classList={{ 'bg-fu-warning/30': isEmpty, 'bg-fu-warning': !isEmpty }}
>
<div class="bx-dec invisible group-hover/item:visible">
<div class="section-dec flex items-center absolute bg-fu-green/90 rounded-[10px] p-[10px] shadow-[0_5px_15px_4px_rgba(0, 135, 90, 0.25)]] mt-[20px] ml-[20px]">
<div class="box-img">
<Dynamic class="mr-[10px] w-20 h-20 text-fu-warning" component={IconPackage} />
<Dynamic
class="mr-[10px] w-20 h-20 text-fu-warning"
component={IconPackage}
/>
</div>
<div class="dec max-w-[200px]">
<h4 class="text-white mb-[5px] text-base font-bold">{isEmpty ? 'Rỗng' : 'Dụng cụ bếp'}</h4>
<h4 class="text-white mb-[5px] text-base font-bold">
{isEmpty ? 'Rỗng' : 'Dụng cụ bếp'}
</h4>
<p class="line-clamp-3">
{isEmpty
? ''

View File

@ -19,6 +19,18 @@ export const ROUTES = [
filter: {},
show: true,
},
{
path: '/house',
components: lazy(() => import('@pages/House')),
filter: {},
show: true,
},
{
path: '/house/create',
components: lazy(() => import('@pages/HouseCreate')),
filter: {},
show: true,
},
{
path: '/warehouse',
components: lazy(() => import('@pages/WareHouse')),