0.1.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight } from 'lucide-react';
|
||||
import { Plus, Star, ArrowDownNarrowWide, ArrowUpNarrowWide, ChevronsLeft, ChevronsRight, Trash2 } from 'lucide-react';
|
||||
|
||||
import AppLayout from '@/Layouts/AppLayout.jsx';
|
||||
|
||||
@@ -23,11 +23,27 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
||||
const favouriteOnClickHandler = (pathword) => {
|
||||
axios.post(route('comics.postFavourite'), { pathword: pathword }).then(res => {
|
||||
setFavourites(res.data);
|
||||
toast({
|
||||
title: "All set",
|
||||
description: `${comic.comic.name} is now in / remove your favorite list.`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "All set",
|
||||
description: `${comic.comic.name} is now in / remove your favorite list.`,
|
||||
const removeAllHistoriesOnClickHandler = (pathword) => {
|
||||
router.visit(route('comics.destroyHistory', { pathword: pathword }), {
|
||||
method: 'DELETE',
|
||||
replace: false,
|
||||
preserveScroll: true,
|
||||
preserveState: true,
|
||||
showProgress: false,
|
||||
only: ['histories'],
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
title: "All set",
|
||||
description: `All histories have been removed.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,6 +117,12 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
||||
<td className="text-right w-24 pr-3">Alias</td>
|
||||
<td>{ comic.comic.alias }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="text-right pr-3">Status</td>
|
||||
<td>
|
||||
{ comic.comic.region.display } / { comic.comic.status.display }
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="text-right pr-3">Category</td>
|
||||
<td>
|
||||
@@ -115,9 +137,9 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
||||
<td className="text-right pr-3">Authors</td>
|
||||
<td>
|
||||
{ comic.comic.author.map(a => (
|
||||
<Badge key={ a.path_word } className="m-2" variant="outline">
|
||||
<Link href={ route('comics.author', [a.path_word]) }>{ a.name }</Link>
|
||||
</Badge>
|
||||
<Badge key={ a.path_word } className="m-2" variant="outline">
|
||||
<Link href={ route('comics.author', [a.path_word]) }>{ a.name }</Link>
|
||||
</Badge>
|
||||
) ) }
|
||||
</td>
|
||||
</tr>
|
||||
@@ -149,10 +171,32 @@ export default function Chapters({ auth, comic, chapters, histories, offset }) {
|
||||
</TabsTrigger>
|
||||
)) }
|
||||
</TabsList>
|
||||
<div>
|
||||
<Button variant="link" size="icon" onClick={ () => toggleAscending() }>
|
||||
{ ascending ? <ArrowDownNarrowWide /> : <ArrowUpNarrowWide /> }
|
||||
</Button>
|
||||
<div className="flex justify-end">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="link" size="icon" onClick={ () => toggleAscending() }>
|
||||
{ ascending ? <ArrowDownNarrowWide /> : <ArrowUpNarrowWide /> }
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Set order as { ascending ? 'Descending' : 'Ascending' }</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="link" size="icon" onClick={ () => removeAllHistoriesOnClickHandler(comic.comic.path_word) }>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Remove all histories for this comic</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<TabsContent value={ group }>
|
||||
|
||||
@@ -2,6 +2,8 @@ import { useState } from 'react';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import AppLayout from '@/Layouts/AppLayout.jsx';
|
||||
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
import { BreadcrumbItem, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import Checkbox from "@/components/Checkbox";
|
||||
@@ -25,19 +27,11 @@ export default function Histories({ auth, histories }) {
|
||||
}
|
||||
|
||||
const datetimeConversion = (iso8601) => {
|
||||
const date = new Date(iso8601);
|
||||
|
||||
const year = date.getUTCFullYear();
|
||||
const month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Months are 0-based
|
||||
const day = String(date.getUTCDate()).padStart(2, '0');
|
||||
const hours = String(date.getUTCHours()).padStart(2, '0');
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
return DateTime.fromISO(iso8601).setZone(auth.user.settings.timezone).toFormat('dd-MM-yyyy HH:mm');
|
||||
}
|
||||
|
||||
const deleteButtonOnClickHandler = () => {
|
||||
router.visit(route('comics.destroyHistories'), {
|
||||
router.visit(route('comics.patchHistories'), {
|
||||
data: (ids.length > 0) ? { ids: ids } : { ids: 'all' },
|
||||
method: "PATCH",
|
||||
only: ['histories'],
|
||||
@@ -50,6 +44,20 @@ export default function Histories({ auth, histories }) {
|
||||
});
|
||||
}
|
||||
|
||||
const removeDuplicatedButtonOnClickHandler = () => {
|
||||
router.visit(route('comics.destroyHistories'), {
|
||||
data: (ids.length > 0) ? { ids: ids } : { ids: 'all' },
|
||||
method: "DELETE",
|
||||
only: ['histories'],
|
||||
onSuccess: data => {
|
||||
toast({
|
||||
title: "All set",
|
||||
description: `The duplicated records has been deleted.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<AppLayout auth={ auth } header={
|
||||
<>
|
||||
@@ -67,6 +75,10 @@ export default function Histories({ auth, histories }) {
|
||||
<Button size="sm" variant="destructive" onClick={ () => deleteButtonOnClickHandler() }>
|
||||
{ ids.length > 0 ? `Delete selected (${ids.length})` : "Delete All" }
|
||||
</Button>
|
||||
|
||||
<Button size="sm" variant="destructive" onClick={ () => removeDuplicatedButtonOnClickHandler() }>
|
||||
Remove duplicates
|
||||
</Button>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
@@ -74,7 +86,7 @@ export default function Histories({ auth, histories }) {
|
||||
<TableHead>Select</TableHead>
|
||||
<TableHead>Chapter</TableHead>
|
||||
<TableHead>Comic</TableHead>
|
||||
<TableHead className="hidden lg:block">Read at</TableHead>
|
||||
<TableHead className="invisible lg:visible">Read at</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -97,7 +109,7 @@ export default function Histories({ auth, histories }) {
|
||||
{ h.comic.name }
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="hidden lg:block">{ datetimeConversion(h.read_at) }</TableCell>
|
||||
<TableCell className="invisible lg:visible">{ datetimeConversion(h.pivot.created_at) }</TableCell>
|
||||
</TableRow>
|
||||
)) }
|
||||
</TableBody>
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function Index({ comics, offset, auth }) {
|
||||
|
||||
const url = new URL(window.location); //searchParams
|
||||
|
||||
const [favourites, setFavourites] = useState(auth.user.favourites);
|
||||
const [favourites, setFavourites] = useState(auth.user?.favourites);
|
||||
const { toast } = useToast();
|
||||
|
||||
const favouriteOnClickHandler = (pathword) => {
|
||||
|
||||
@@ -11,6 +11,20 @@ export default function Updates({ auth }) {
|
||||
<title>Updates</title>
|
||||
</Head>
|
||||
<div className="p-3 pt-1">
|
||||
<Card className="w-[90%] m-3 mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle>0.1.0</CardTitle>
|
||||
<CardDescription>Release: 04 Jan 2025</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul>
|
||||
<li>iOS fixed for search auto fours</li>
|
||||
<li>Beta: Timezones, only available for histories now</li>
|
||||
<li>Beta: Remove histories for selected comic</li>
|
||||
<li>Beta: Remove duplicated reading histories</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="w-[90%] m-3 mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle>0.0.2 FR1</CardTitle>
|
||||
|
||||
@@ -6,7 +6,7 @@ import AppLayout from "@/Layouts/AppLayout.jsx";
|
||||
import { BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator } from "@/components/ui/breadcrumb.jsx";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
export default function Edit({ auth, mustVerifyEmail, status }) {
|
||||
export default function Edit({ auth, mustVerifyEmail, status, timezones }) {
|
||||
return (
|
||||
<AppLayout auth={ auth } header={
|
||||
<>
|
||||
@@ -27,7 +27,7 @@ export default function Edit({ auth, mustVerifyEmail, status }) {
|
||||
<TabsContent value="profile">
|
||||
<UpdateProfileInformationForm
|
||||
mustVerifyEmail={ mustVerifyEmail }
|
||||
status={ status }
|
||||
status={ status } timezones={ timezones }
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="password">
|
||||
|
||||
@@ -11,24 +11,29 @@ import {
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
} from '@/components/ui/card';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
|
||||
export default function UpdateProfileInformation({
|
||||
mustVerifyEmail,
|
||||
status,
|
||||
className = '',
|
||||
}) {
|
||||
export default function UpdateProfileInformation({ mustVerifyEmail, status, timezones }) {
|
||||
const user = usePage().props.auth.user;
|
||||
|
||||
const { data, setData, patch, errors, processing, recentlySuccessful } =
|
||||
useForm({
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
timezone: user.settings.timezone
|
||||
});
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
patch(route('profile.update'));
|
||||
};
|
||||
|
||||
@@ -51,6 +56,7 @@ export default function UpdateProfileInformation({
|
||||
</div>
|
||||
<InputError className="mt-2" message={ errors.name } />
|
||||
</div>
|
||||
|
||||
<div className="grid w-full items-center gap-4 pt-3">
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
<InputLabel htmlFor="email" value="Email" />
|
||||
@@ -88,6 +94,28 @@ export default function UpdateProfileInformation({
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
|
||||
<div className="grid w-full items-center gap-4 pt-3">
|
||||
<div className="flex flex-col space-y-1.5">
|
||||
<InputLabel htmlFor="timezone" value="Timezone" />
|
||||
<Select defaultValue={ data.timezone } onValueChange={ (e) => setData('timezone', e) } className="w-full">
|
||||
<SelectTrigger className="border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:focus:border-indigo-600 dark:focus:ring-indigo-600">
|
||||
<SelectValue placeholder="Select a timezone" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{ Object.keys(timezones).map((area, i) => (
|
||||
<SelectGroup key={ i }>
|
||||
<SelectLabel>{ area }</SelectLabel>
|
||||
{ Object.keys(timezones[area]).map((tz, j) => (
|
||||
<SelectItem className="pl-5" key={ j } value={ tz }>{ timezones[area][tz] }</SelectItem>
|
||||
) ) }
|
||||
</SelectGroup>
|
||||
) ) }
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<InputError className="mt-2" message={ errors.timezone } />
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<PrimaryButton disabled={ processing }>Save</PrimaryButton>
|
||||
|
||||
@@ -57,18 +57,20 @@ export function AppSidebar({ auth }) {
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 leading-none">
|
||||
<span className="font-semibold">Comic</span>
|
||||
<span>0.0.2 FR1</span>
|
||||
<span>0.1.0</span>
|
||||
</div>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
<SidebarGroup className="py-0">
|
||||
<SidebarGroupContent className="relative">
|
||||
<form autoCorrect="false" autoComplete="false" autoFocus="false" onSubmit={ (e) => searchOnSubmitHandler(e)} >
|
||||
<SidebarGroupContent className="relative" tabIndex="0">
|
||||
<form autoCorrect="false" onSubmit={ (e) => searchOnSubmitHandler(e) }>
|
||||
<label htmlFor="search" className="sr-only">Search</label>
|
||||
<SidebarInput onChange={ (e) => setSearch(e.target.value) } id="search" placeholder="Search" className="pl-8" />
|
||||
<Search className="pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-50" />
|
||||
<SidebarInput onChange={ (e) => setSearch(e.target.value) } id="search"
|
||||
placeholder="Search" className="pl-8" />
|
||||
<Search
|
||||
className="pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-50" />
|
||||
</form>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
|
||||
Reference in New Issue
Block a user