Merge remote-tracking branch 'origin/master' into feature/slicing

This commit is contained in:
fredy.siswanto 2025-03-15 01:02:32 +07:00
commit 3ddc657cfb
21 changed files with 77 additions and 71 deletions

View File

@ -71,7 +71,7 @@ export const DialogDelete = (properties: TProperties) => {
>
<Button
type="submit"
variant="newsDanger"
variant="danger"
className="text-md h-[42px] rounded-md"
disabled={fetcher.state !== 'idle'}
isLoading={fetcher.state !== 'idle'}

View File

@ -92,7 +92,7 @@ export const DialogSuccess = ({ isOpen, onClose }: ModalProperties) => {
/>
<Button
className="mt-5 w-full rounded-md"
variant="newsPrimary"
variant="primary"
as={Link}
to="/"
onClick={onClose}
@ -111,7 +111,7 @@ export const DialogSuccess = ({ isOpen, onClose }: ModalProperties) => {
{userData ? (
<Button
className="mt-5 w-full rounded-md"
variant="newsSecondary"
variant="outline"
onClick={() => {
onClose()
setIsSubscribeOpen(true)
@ -122,7 +122,7 @@ export const DialogSuccess = ({ isOpen, onClose }: ModalProperties) => {
) : (
<Button
className="mt-5 w-full rounded-md"
variant="newsPrimary"
variant="primary"
onClick={() => {
onClose()
setIsLoginOpen(true)

View File

@ -1,25 +0,0 @@
import type { JSX, SVGProps } from 'react'
/**
* Note: `ChevronIcon` default mengarah ke bawah.
* Gunakan class `rotate-xx` untuk mengubah arah ikon.
*/
export const ChevronIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={21}
height={21}
viewBox="0 0 21 21"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={properties.className}
{...properties}
>
<path
d="M10.197 13.623l5.008-5.008-1.177-1.18-3.83 3.834-3.831-3.833-1.178 1.178 5.008 5.009z"
fill="currentColor"
/>
</svg>
)
}

View File

@ -21,7 +21,7 @@ import {
StrikethroughIcon,
SwatchIcon,
XCircleIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import {
Bars3BottomCenterIcon,
QuotationMarkIcon,

View File

@ -1,4 +1,4 @@
import { CodeBracketSquareIcon } from '@heroicons/react/20/solid'
import { CodeBracketSquareIcon } from '@heroicons/react/24/solid'
import MonacoEditor from '@monaco-editor/react'
import type { Dispatch, SetStateAction } from 'react'
import { Controller } from 'react-hook-form'

View File

@ -1,5 +1,5 @@
import { Button as HeadlessButton } from '@headlessui/react'
import { ArrowPathIcon } from '@heroicons/react/20/solid'
import { ArrowPathIcon } from '@heroicons/react/24/solid'
import { cva, type VariantProps } from 'class-variance-authority'
import type { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react'
import { twMerge } from 'tailwind-merge'
@ -9,16 +9,18 @@ const buttonVariants = cva(
{
variants: {
variant: {
newsPrimary:
primary:
'bg-[#2E2F7C] text-white text-lg hover:bg-[#4C5CA0] hover:shadow transition active:bg-[#6970B4]',
newsDanger:
danger:
'bg-[#EF4444] text-white text-lg hover:shadow transition active:bg-[#FEE2E2] hover:bg-[#FCA5A5]',
newsPrimaryOutline:
primaryOutline:
'border-[3px] bg-[#2E2F7C] border-white text-white text-lg hover:bg-[#4C5CA0] hover:shadow-lg active:shadow-2xl transition active:bg-[#6970B4]',
newsSecondary:
outline:
'border-[3px] bg-white hover:shadow-lg active:shadow-2xl border-[#2E2F7C] text-[#2E2F7C] hover:text-[#4C5CA0] active:text-[#6970B4] text-lg hover:border-[#4C5CA0] transition active:border-[#6970B4]',
icon: '',
link: 'font-semibold text-[#2E2F7C] hover:text-[#4C5CA0] active:text-[#6970B4] transition',
secondary:
'hover:bg-[#707FDD]/10 active:bg-[#707FDD]/20 hover:text-[#707FDD] text-[#273240]',
},
size: {
default: 'h-[50px] w-[150px]',
@ -30,7 +32,7 @@ const buttonVariants = cva(
},
},
defaultVariants: {
variant: 'newsPrimary',
variant: 'primary',
size: 'default',
},
},
@ -42,6 +44,7 @@ type ButtonBaseProperties = {
size?: VariantProps<typeof buttonVariants>['size']
className?: string
isLoading?: boolean
icon?: ReactNode
}
type PolymorphicReference<C extends ElementType> =
@ -62,6 +65,7 @@ export const Button = <C extends ElementType = 'button'>(
size,
className,
isLoading = false,
icon,
...restProperties
} = properties
const Component = as || HeadlessButton
@ -72,7 +76,7 @@ export const Button = <C extends ElementType = 'button'>(
className={classes}
{...restProperties}
>
{isLoading && <ArrowPathIcon className="animate-spin" />}
{isLoading ? <ArrowPathIcon className="size-5 animate-spin" /> : icon}
{children}
</Component>
)

View File

@ -7,7 +7,7 @@ import {
ComboboxOptions,
ComboboxOption,
} from '@headlessui/react'
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/24/solid'
import { useState, type ComponentProps, type ReactNode } from 'react'
import {
get,

View File

@ -1,5 +1,5 @@
import { Field, Label, Input as HeadlessInput } from '@headlessui/react'
import { CloudArrowUpIcon } from '@heroicons/react/20/solid'
import { CloudArrowUpIcon } from '@heroicons/react/24/solid'
import { useEffect, type ComponentProps, type ReactNode } from 'react'
import { get, type FieldError, type RegisterOptions } from 'react-hook-form'
import { useRemixFormContext } from 'remix-hook-form'

View File

@ -45,7 +45,7 @@ export const Newsletter = (property: NewsletterProperties) => {
/>
<Button
type="submit"
variant="newsPrimary"
variant="primary"
size="block"
>
Subscribe

View File

@ -1,4 +1,4 @@
import { LinkIcon } from '@heroicons/react/20/solid'
import { LinkIcon } from '@heroicons/react/24/solid'
import { useState } from 'react'
import {
FacebookShareButton,

View File

@ -1,5 +1,5 @@
import { Field, Input, Label, Select } from '@headlessui/react'
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid'
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'
import { useState } from 'react'
interface SearchFilterProperties {

View File

@ -19,6 +19,8 @@ type AdminContextProperties = {
setIsUploadOpen: Dispatch<SetStateAction<TUpload>>
uploadedFile?: string
setUploadedFile: Dispatch<SetStateAction<string | undefined>>
editProfile: boolean
setEditProfile: Dispatch<SetStateAction<boolean>>
}
const AdminContext = createContext<AdminContextProperties | undefined>(
@ -28,6 +30,7 @@ const AdminContext = createContext<AdminContextProperties | undefined>(
export const AdminProvider = ({ children }: PropsWithChildren) => {
const [isUploadOpen, setIsUploadOpen] = useState<TUpload>()
const [uploadedFile, setUploadedFile] = useState<string | undefined>()
const [editProfile, setEditProfile] = useState(false)
return (
<AdminContext.Provider
@ -36,6 +39,8 @@ export const AdminProvider = ({ children }: PropsWithChildren) => {
setIsUploadOpen,
uploadedFile,
setUploadedFile,
editProfile,
setEditProfile,
}}
>
{children}

View File

@ -7,7 +7,7 @@ import {
PresentationChartLineIcon,
TagIcon,
UsersIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import type { SVGProps } from 'react'
type TMenu = {

View File

@ -1,16 +1,22 @@
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import {
ArrowRightStartOnRectangleIcon,
UserIcon,
} from '@heroicons/react/24/outline'
import { ChevronDownIcon } from '@heroicons/react/24/solid'
import { Link, useFetcher, useRouteLoaderData } from 'react-router'
import { ChevronIcon } from '~/components/icons/chevron'
import { ProfileIcon } from '~/components/icons/profile'
import { Button } from '~/components/ui/button'
import { APP } from '~/configs/meta'
import { useAdminContext } from '~/contexts/admin'
import type { loader } from '~/routes/_admin.lg-admin'
export const Navbar = () => {
const loaderData = useRouteLoaderData<typeof loader>('routes/_admin.lg-admin')
const { staffData } = loaderData || {}
const fetcher = useFetcher()
const { setEditProfile } = useAdminContext()
return (
<div className="flex h-20 items-center justify-between border-b border-[#ECECEC] bg-white px-10 py-5">
@ -40,13 +46,26 @@ export const Navbar = () => {
<span className="text-sm">{staffData?.name}</span>
</div>
<ChevronIcon className="opacity-50" />
<ChevronDownIcon className="size-4 opacity-50" />
</PopoverButton>
<PopoverPanel
anchor={{ to: 'bottom', gap: '8px' }}
transition
className="flex w-3xs flex-col rounded-xl border border-[#ECECEC] bg-white p-3 transition duration-200 ease-in-out data-[closed]:-translate-y-1 data-[closed]:opacity-0"
className="flex w-3xs flex-col divide-y divide-black/5 rounded-xl border border-[#ECECEC] bg-white transition duration-200 ease-in-out data-[closed]:-translate-y-1 data-[closed]:opacity-0"
>
<div className="p-2">
<Button
variant="secondary"
className="w-full justify-start rounded p-1 px-3 text-lg font-semibold"
onClick={() => {
setEditProfile(true)
}}
>
<UserIcon className="size-5" />
<span>Profile</span>
</Button>
</div>
<div className="p-2">
<fetcher.Form
method="POST"
action="/actions/admin/logout"
@ -56,11 +75,14 @@ export const Navbar = () => {
disabled={fetcher.state !== 'idle'}
isLoading={fetcher.state !== 'idle'}
type="submit"
className="w-full rounded p-1"
className="w-full justify-start rounded p-1 px-3 text-lg font-semibold"
variant="secondary"
icon={<ArrowRightStartOnRectangleIcon className="size-5" />}
>
Logout
<span>Logout</span>
</Button>
</fetcher.Form>
</div>
</PopoverPanel>
</Popover>
</div>

View File

@ -34,7 +34,7 @@ export const FooterNewsletter = () => {
/>
<Button
type="submit"
variant="newsPrimaryOutline"
variant="primaryOutline"
size="block"
>
Subscribe

View File

@ -70,7 +70,7 @@ export const HeaderMenuMobile = (properties: THeaderMenuMobile) => {
action="/actions/logout"
>
<Button
variant="newsSecondary"
variant="outline"
className="w-full px-[35px] py-3 text-center sm:hidden"
type="submit"
>
@ -79,7 +79,7 @@ export const HeaderMenuMobile = (properties: THeaderMenuMobile) => {
</fetcher.Form>
) : (
<Button
variant="newsSecondary"
variant="outline"
className="w-full px-[35px] py-3 text-center sm:hidden"
onClick={() => {
setIsMenuOpen(false)

View File

@ -33,7 +33,7 @@ export const HeaderTop = () => {
action="/actions/logout"
>
<Button
variant="newsSecondary"
variant="outline"
className="hidden sm:flex"
type="submit"
disabled={fetcher.state !== 'idle'}
@ -44,7 +44,7 @@ export const HeaderTop = () => {
</fetcher.Form>
) : (
<Button
variant="newsSecondary"
variant="outline"
className="hidden sm:block"
onClick={() => setIsLoginOpen(true)}
>

View File

@ -2,7 +2,7 @@ import {
PencilSquareIcon,
PlusIcon,
TrashIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import type { ConfigColumns } from 'datatables.net-dt'
import type { DataTableSlots } from 'datatables.net-react'
import { useState } from 'react'
@ -79,7 +79,7 @@ export const AdvertisementsPage = () => {
<Button
type="button"
size="icon"
variant="newsDanger"
variant="danger"
onClick={() => setSelectedAds(data)}
title="Hapus Banner Iklan"
>

View File

@ -2,7 +2,7 @@ import {
PencilSquareIcon,
PlusIcon,
TrashIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import DT, { type Config, type ConfigColumns } from 'datatables.net-dt'
import DataTable, { type DataTableSlots } from 'datatables.net-react'
import { useState } from 'react'
@ -78,7 +78,7 @@ export const CategoriesPage = () => {
<Button
type="button"
size="icon"
variant="newsDanger"
variant="danger"
onClick={() => setSelectedCategory(data)}
title="Hapus Kategori"
>

View File

@ -2,7 +2,7 @@ import {
PencilSquareIcon,
PlusIcon,
TrashIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import DT from 'datatables.net-dt'
import DataTable from 'datatables.net-react'
import { useState } from 'react'
@ -91,7 +91,7 @@ export const SubscribePlanPage = () => {
<Button
type="button"
size="icon"
variant="newsDanger"
variant="danger"
onClick={() => setSelectedSubscribePlan(data)}
title="Hapus Subscribe Plan"
>

View File

@ -2,7 +2,7 @@ import {
PencilSquareIcon,
PlusIcon,
TrashIcon,
} from '@heroicons/react/20/solid'
} from '@heroicons/react/24/solid'
import DT, { type Config, type ConfigColumns } from 'datatables.net-dt'
import DataTable, { type DataTableSlots } from 'datatables.net-react'
import { useState } from 'react'
@ -64,7 +64,7 @@ export const TagsPage = () => {
<Button
type="button"
size="icon"
variant="newsDanger"
variant="danger"
onClick={() => setSelectedTag(data)}
title="Hapus Tag"
>