diff --git a/src/api/api.ts b/src/api/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ab3b37b6ae8cb490057ca7c8f3f6387b85191b2 --- /dev/null +++ b/src/api/api.ts @@ -0,0 +1,14 @@ +import axios from "axios"; + +const restApiUrl: string = import.meta.env.VITE_REST_API_URL; + +const api = axios.create({ + baseURL: restApiUrl, + headers: { + "Authorization": "Bearer " + sessionStorage.getItem("accessToken"), + }, + withCredentials : true, + timeout: 1000, +}); + +export default api; \ No newline at end of file diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 8bf509c2559c26a353c1ea16f796c04fa164f4db..08c1482d89a78427aed38ddc759ba0defe664389 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -2,6 +2,7 @@ import logo from '../assets/images/logo.svg'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPlayCircle, faUser, } from '@fortawesome/free-solid-svg-icons'; import {NavLink} from "react-router-dom"; +import {cn} from "@/lib/utils.ts"; // import {useAuth} from "@/TonalityApp.tsx"; // import {NavLink, useNavigate} from 'react-router-dom'; // import profile from '../assets/images/user.png'; @@ -20,10 +21,10 @@ const Sidebar = () => { <div className="menu h-screen"> <nav> <ul className='text-left'> - <NavLink to="/album" className="mb-2 hover:bg-gray-700 p-2 rounded cursor-pointer flex items-center"> + <NavLink to="/album" className={cn("mb-2 hover:bg-gray-700 p-2 rounded cursor-pointer flex items-center", location.pathname === "/album" && "bg-gray-700")}> <FontAwesomeIcon icon={faPlayCircle} className="mr-2" /> Premium Albums </NavLink> - <NavLink to="/subscription" className="mb-2 hover:bg-gray-700 p-2 rounded cursor-pointer flex items-center"> + <NavLink to="/subscription" className={cn("mb-2 hover:bg-gray-700 p-2 rounded cursor-pointer flex items-center", location.pathname === "/subscription" && "bg-gray-700")}> <FontAwesomeIcon icon={faUser} className="mr-2" /> Subscription </NavLink> </ul> diff --git a/src/components/songs-table.tsx b/src/components/songs-table.tsx index 23323aff5a566d400a9c51d4bf254c0669eb98bd..863c523b685cc0166feb1c0ed0ab70526c670d06 100644 --- a/src/components/songs-table.tsx +++ b/src/components/songs-table.tsx @@ -4,12 +4,12 @@ import { SongDropdown } from "./songs-dropdown"; interface PremiumSong { songId: number; albumId: number; - title: String; - artist: String; + title: string; + artist: string; songNumber: number; discNumber:number; duration:number; - audioFilename: String; + audioFilename: string; } export function TableSongs( data: PremiumSong[] ) { diff --git a/src/components/subcription-dropdown.tsx b/src/components/subcription-dropdown.tsx new file mode 100644 index 0000000000000000000000000000000000000000..54b23722f6e7c12bef78a666a691a5db4877eeda --- /dev/null +++ b/src/components/subcription-dropdown.tsx @@ -0,0 +1,23 @@ +import {DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu.tsx"; +import { Button } from "@/components/ui/button.tsx"; + +export function SubscriptionDropdown({handler}: {handler: (accept: boolean) => void}) { + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button variant="outline" className='bg-zinc-800 border-none text-white rounded h-3 pb-5'> + ... + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent className="w-40 text-white bg-black border-gray-200" style={{ position: 'absolute', left: -20 }}> + <DropdownMenuItem onClick={() => handler(true)} className='hover:bg-zinc-800'> + Accept + </DropdownMenuItem> + <DropdownMenuItem onClick={() => handler(false)}> + Reject + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ) +} \ No newline at end of file diff --git a/src/components/subscription-table.tsx b/src/components/subscription-table.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3c216d10f344deadaa1fc8c41a5d37c403bf9a42 --- /dev/null +++ b/src/components/subscription-table.tsx @@ -0,0 +1,52 @@ +import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table.tsx"; +import {SubscriptionDropdown} from "@/components/subcription-dropdown.tsx"; +import api from "@/api/api.ts"; + +interface Subscription { + userId: number; + premiumAlbumId: number; + username: string; + albumName: string; + artist: string; + status: string; + createdAt: string; + updatedAt: string; +} + +export function SubscriptionTable({data}: { data: Subscription[] }) { + return ( + <Table className="text-white ml-20 w-[790px] border"> + <TableHeader> + <TableRow className="hover:bg-transparent"> + <TableHead className="w-[500px]">Album Name</TableHead> + <TableHead className="w-[500px]">Artist</TableHead> + <TableHead className="w-[500px]">Username</TableHead> + <TableHead className="w-[500px]">Status</TableHead> + <TableHead className="w-[10px]"></TableHead> + </TableRow> + </TableHeader> + <TableBody className="text-left"> + {data.map((subscription: Subscription, index) => ( + <TableRow key={index} className="hover:bg-transparent"> + <TableCell>{subscription.albumName}</TableCell> + <TableCell>{subscription.artist}</TableCell> + <TableCell>{subscription.username}</TableCell> + <TableCell>{subscription.status}</TableCell> + <TableCell>{subscription.status == "PENDING" + ? <SubscriptionDropdown handler={(accept) => { + api.post( + "subscription", + { + userId: subscription.userId, + premiumAlbumId: subscription.premiumAlbumId, + status: accept ? "ACTIVE" : "REJECTED" + }, + ) + }}/> : null}</TableCell> + </TableRow> + ))} + </TableBody> + + </Table> + ) +} \ No newline at end of file diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 6504d958d1297c79a1072fb52684785d8a323074..0756d91b3a6242ac26c8d73345ab7f8a60209db4 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -1,5 +1,3 @@ -"use client"; - import * as z from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -15,13 +13,12 @@ import { import { Input } from "@/components/ui/input.tsx"; import { Button } from "@/components/ui/button.tsx"; import { Link } from "react-router-dom"; -import { storeAccessToken } from "@/utils/token.ts"; import { StatusCodes } from "http-status-codes"; - -const restApiUrl: string = import.meta.env.VITE_REST_API_URL; +import {useAuth} from "@/TonalityApp.tsx"; +import api from "@/api/api.ts"; const isUsernameInvalid = async (username: string): Promise<boolean> => { - const res = await axios.post(restApiUrl + "username-availability", { + const res = await api.post("username-availability", { username: username, }); @@ -64,19 +61,25 @@ const LoginPage = () => { // Define submit handler async function onSubmit(values: z.infer<typeof loginFormSchema>) { + console.log("values", values); + const { onLogin } = useAuth(); try { - const res = await axios.post( - restApiUrl + "login", + const res = await api.post( + "login", { username: values.username, password: values.password, }, { - withCredentials: true, + headers: { + "access-control-expose-headers" : "Set-Cookie", + } }, ); - storeAccessToken(res.data.accessToken); + console.log("res", res.data.accessToken) + + onLogin(res.data.accessToken); } catch (err) { if ( axios.isAxiosError(err) && diff --git a/src/pages/SignUpPage.tsx b/src/pages/SignUpPage.tsx index d5eb1f6ad0b629d26691ec41f7e0043858e1ba89..41d735c4d82aab6fc08e342670dcaa1360df1b11 100644 --- a/src/pages/SignUpPage.tsx +++ b/src/pages/SignUpPage.tsx @@ -1,4 +1,4 @@ -"use client"; +// "use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; diff --git a/src/pages/SubscriptionPage.tsx b/src/pages/SubscriptionPage.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e3981bac9774b32834f01b7846f15e5cf399a3d8 --- /dev/null +++ b/src/pages/SubscriptionPage.tsx @@ -0,0 +1,49 @@ +import React, {useEffect, useState} from 'react'; +import axios from "axios"; +import {SubscriptionTable} from "@/components/subscription-table.tsx"; +import api from "@/api/api.ts"; + +interface Subscription { + userId: number; + premiumAlbumId: number; + username: string; + albumName: string; + artist: string; + status: string; + createdAt: string; + updatedAt: string; +} + +const SubscriptionPage = () => { + const [subscriptionData, setSubscriptionData] : [Subscription[], (subscriptionData: Subscription[]) => void] = useState([]); + const [loading, setLoading] = useState(true); + useEffect(() => { + // get current page from query params + const params = new URLSearchParams(window.location.search); + const page = params.get('page') || 1; + const fetchData = async () => { + try { + const response = await api.get('subscription' + '?page=' + page,); + setSubscriptionData(response.data.subscription ?? []) + setLoading(false); + } catch (error) { + console.error('Error fetching data:', error); + setLoading(true) + } + } + fetchData() + }, []); + + + return ( + <div className='mt-2 w-800 flex flex-col items-center '> + <div> + { + loading ? <div className="text-white">Loading . . .</div> : <SubscriptionTable data={subscriptionData} /> + } + </div> + </div> + ); +}; + +export default SubscriptionPage; \ No newline at end of file diff --git a/src/pages/subscription-page.tsx b/src/pages/subscription-page.tsx deleted file mode 100644 index 6d898e826e679904b9044da9e24ddb79fd8829f0..0000000000000000000000000000000000000000 --- a/src/pages/subscription-page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -const SubscriptionPage = () => { - return ( - <div> - asdf - </div> - ); -}; - -export default SubscriptionPage; \ No newline at end of file diff --git a/src/routes/routes.ts b/src/routes/routes.ts index c8704b223f5785291c9aa48968303bcfbee0cbd3..8a1e6077ff9ecb33e0ec2aac99dac1e4314ff054 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -12,6 +12,7 @@ import NotMatchLayout from "@/layouts/NotMatchLayout.tsx"; import NotMatch from "@/pages/NotMatch.tsx"; import DeleteAlbumDialog from "@/components/delete-dialog-album"; import DeleteSongDialog from "@/components/delete-dialog-song"; +import SubscriptionPage from "@/pages/SubscriptionPage.tsx"; export const routes = [ { @@ -98,6 +99,12 @@ export const routes = [ }, ], }, + { + name: "subscription", + title: "Subscription", + component: SubscriptionPage, + path: "/subscription", + } ], }, {