Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ad group functionalities #37

Merged
merged 2 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions app/(root)/group/[id]/update/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';
import { fetchCampaignList,fetchGroup } from '@/lib/api';
import { Campaign } from '@/lib/definitions';
import { useEffect, useState } from 'react';
import Form from '@/components/ui/form-update-group';

export default function Page({ params }: { params: { id: string } }) {
const id = params.id;
const [adg, setAdg] = useState<any|null>(null);
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
useEffect(() => {
fetchCampaignList().then(data => setCampaigns(data.result.filter((adc:any) => adc.status !== 'deleted')));
}, []);
useEffect(() => {
fetchGroup(id).then(data => setAdg(data.result));
}, [id]);
return (
<main>
{adg &&<Form campaigns={campaigns} adg={adg}/>}
</main>
);
}

18 changes: 18 additions & 0 deletions app/(root)/group/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client'
import { fetchCampaignList } from '@/lib/api';
import { Campaign} from '@/lib/definitions';
import { useEffect, useState } from 'react';
import Form from '@/components/ui/form-create-group';

export default function Page() {
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
useEffect(() => {
fetchCampaignList().then(data => setCampaigns(data.result.filter((ad:any) => ad.status !== 'deleted')));
}, []);
return (
<main className='mx-auto'>
<Form campaigns={campaigns} />
</main>
);
}

115 changes: 115 additions & 0 deletions app/(root)/group/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"use client"
import Pagination from '@/components/ui/pagination';
import Search from '@mitech/shared-components/ui/search';
import { CreateAdGroup } from '@/components/ui/button';
import { useEffect, useState } from 'react';
import { GetFilteredAdGroupPages } from '@/lib/data';
import { fetchCampaignList, fetchGroupList } from '@/lib/api';
import { Campaign } from '@/lib/definitions';
import 'react-toastify/dist/ReactToastify.css';
import Cookies from 'js-cookie';
import { toast } from 'react-toastify';
import AdGroupTable from '@/components/ui/ad-group-table';

export default function Page({
searchParams,
}: {
searchParams?: {
query?: string;
page?: string;
};
}) {
const query = searchParams?.query || '';
const currentPage = Number(searchParams?.page) || 1;
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
useEffect(() => {
fetchCampaignList().then(data => setCampaigns(data.result.filter((ad:any) => ad.status !== 'deleted')));
}, []);
const [campaignId, setCampaignId] = useState<string>('');
const [campaignName, setCampaignName] = useState<string>('');
const [adGroupAll, setAdGroupAll] = useState<any[]>([]);
useEffect(() => {
const message = Cookies.get('notification_create_adgroup');
function showToast() {
toast.success(message);
}
if (message) {
setTimeout(showToast, 1000);
Cookies.remove('notification_create_adgroup');
}
}, []);
useEffect(() => {
const message = Cookies.get('notification_update_adgroup');
function showToast() {
toast.success(message);
}
if (message) {
setTimeout(showToast, 1000);
Cookies.remove('notification_update_adgroup');
}
}, []);
useEffect(() => {
const message = Cookies.get('notification_delete_adgroup');
function showToast() {
toast.success(message);
}
if (message) {
setTimeout(showToast, 1000);
Cookies.remove('notification_delete_adgroup');
}
}, []);
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const selectedOption = event.target.selectedOptions[0];
const campaignName = selectedOption.value;
const campaignId = selectedOption.getAttribute('data-id') as string;
if(campaignId !== ''){
setCampaignId(campaignId);
setAdGroupAll([]);
fetchGroupList(campaignId).then(data => setAdGroupAll(data.result));
}
if(campaignName !== ''){
setCampaignName(campaignName);
}
};
const totalPages = GetFilteredAdGroupPages(adGroupAll,query);
return (
<div className="w-full">
<header className="flex items-center justify-between px-6 py-8 bg-gray-900 text-white">
<div className="flex items-center space-x-4">
<h2 className="text-lg font-medium">Manage Ad Group</h2>
</div>
</header>
<div className='w-full max-w-[80%] justify-center items-center m-auto'>
<div className="mt-4 flex items-center justify-center gap-2 md:mt-8">
<select
id="adChannel"
name="channel"
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-2 text-sm outline-2 placeholder:text-gray-500"
defaultValue=""
aria-describedby="channel-error"
onChange={handleChange}
>
<option value="" disabled>
Select a campaign
</option>
{campaigns.map((campaign: Campaign) => (
<option key={campaign.id} value={campaign.name} data-id={campaign.id}>
{campaign.name}
</option>
))}
</select>
<CreateAdGroup />
</div>
</div>
{campaignId !== '' && <div className='w-full max-w-[80%] justify-center items-center m-auto'>
<div className="mt-4 flex items-center justify-center gap-2 md:mt-8">
<Search placeholder="Search ad..." />
</div>
<AdGroupTable query={query} currentPage={currentPage} campaignId={campaignId} campaignName={campaignName}/>
<div className="mt-5 flex w-full justify-center">
<Pagination totalPages={totalPages} />
</div>
</div>}
</div>
);
}
110 changes: 110 additions & 0 deletions components/ui/ad-group-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client';
import { UpdateAdGroup, DeleteAdGroup } from '@/components/ui/button';
import { fetchGroupList } from '@/lib/api';
import {GetFilteredAdGroups, GetFilteredAds } from '@/lib/data';
import { useEffect, useState } from 'react';

export default function AdGroupTable({
query,
currentPage,
campaignId,
campaignName,
}: {
query: string,
currentPage: number,
campaignId:string,
campaignName:string,
}) {
const [sortColumn, setSortColumn] = useState("name");
const [sortOrder, setSortOrder] = useState("asc");
const [adGroupAll, setAdGroupAll] = useState<any[]>([]);
useEffect(() => {
fetchGroupList(campaignId).then(data => setAdGroupAll(data.result));
}, [campaignId]);
const handleSort = (column:string) => {
if (sortColumn === column) {
setSortOrder(sortOrder === "asc" ? "desc" : "asc");
} else {
setSortColumn(column);
setSortOrder("asc");
}
};
const sortSvg = (column:string) =>
sortColumn === column ? (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
{sortOrder === "desc" ? (
<path d="M7 10l5 5 5-5z" />
) : (
<path d="M7 14l5-5 5 5z" />
)}

<path d="M0 0h24v24H0z" fill="none" />
</svg>
) : null;
const adGroups = GetFilteredAdGroups(adGroupAll, query, currentPage, sortColumn,sortOrder);
return (
<div className="mt-6 flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<div className="overflow-hidden rounded-md p-2 md:pt-0">
<table className="hidden min-w-full rounded-md text-gray-900 md:table">
<thead className="rounded-md bg-gray-50 text-left text-sm font-normal">
<tr>
<th scope="col" onClick={() => handleSort("name")} className="px-4 py-5 font-medium sm:pl-6">
<p className='inline-flex cursor-pointer font-bold'>Name{sortSvg("name")}</p>
</th>
<th scope="col" onClick={() => handleSort("campaign")} className="px-3 py-5 font-medium">
<p className='inline-flex cursor-pointer font-bold'>Canpaign{sortSvg("campaign")}</p>
</th>
<th scope="col" onClick={() => handleSort("notes")} className="px-3 py-5 font-medium">
<p className='inline-flex cursor-pointer font-bold'>Notes{sortSvg("notes")}</p>
</th>
<th scope="col" className="relative py-3 pl-2 pr-2 w-0.5">
<span className="sr-only">Edit</span>
</th>
<th scope="col" className="relative py-3 pl-2 pr-2 w-0.5">
<span className="sr-only">Delete</span>
</th>
</tr>
</thead>

<tbody className="divide-y divide-gray-200 text-gray-900">
{adGroups.map((adg) => (
<tr key={adg.id} className="group">
<td className="whitespace-nowrap bg-white py-5 pl-4 pr-3 text-sm text-black group-first-of-type:rounded-md group-last-of-type:rounded-md sm:pl-6">
<div className="flex items-center gap-3">
<p>{adg.name}</p>
</div>
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{campaignName}
</td>

<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{adg.notes}
</td>
<td className="whitespace-nowrap py-3 pl-2 pr-2 w-0.5">
<div className="flex gap-3">
{adg.id && <UpdateAdGroup id={adg.id} />}
</div>
</td>
<td className="whitespace-nowrap py-3 pl-2 pr-2 w-0.5">
<div className="flex gap-3">
{adg.id &&<DeleteAdGroup id={adg.id} />}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
73 changes: 72 additions & 1 deletion components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Link from 'next/link';
import { PlusIcon, PencilIcon, StopIcon, EyeIcon, TrashIcon, ArrowUpOnSquareStackIcon } from '@heroicons/react/24/outline';
import {Tooltip, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure} from "@nextui-org/react";
import { redirect, useRouter } from 'next/navigation';
import { deleteAd, publishAd, unpublishAd } from '@/lib/api';
import { deleteAd, deleteGroup, publishAd, unpublishAd } from '@/lib/api';
import { toast } from 'react-toastify';
import Cookies from 'js-cookie';

Expand Down Expand Up @@ -181,3 +181,74 @@ export function DeleteAd({ id }: { id: string }) {
</form>
);
}

export function CreateAdGroup() {
return (
<Link
href="/group/create"
className="flex h-10 items-center w-56 rounded-lg bg-blue-600 px-4 text-sm font-medium text-white transition-colors hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
>
<span className="hidden md:block">Create Ad Group</span>{' '}
<PlusIcon className="h-5 md:ml-4" />
</Link>
);
}

export function UpdateAdGroup({ id }: { id: string }) {
const router = useRouter();
const handleRedirect = () => {
router.push(`/group/${id}/update`);
};
return (
<Tooltip content={"Edit"} offset={-4}>
<Button onClick={handleRedirect}
className="rounded-md border p-2 hover:bg-gray-100 min-w-0">
<PencilIcon className="w-5" />
</Button>
</Tooltip>
);
}


export function DeleteAdGroup({ id }: { id: string }) {
const deleteAdWithId = "Deleted the Ad Group";
const {isOpen, onOpen, onClose, onOpenChange} = useDisclosure();
const deleteAdHandler = async (id:string) => {
await deleteGroup(id);
Cookies.set('notification_delete_adgroup', 'The ad group is deleted successfully!');
onClose(); // Close the modal
location.reload();
};
return (
<form action={deleteAdWithId}>
<Tooltip content={"Delete"} offset={-4}>
<Button className="rounded-md border p-2 hover:bg-gray-100 min-w-0" onPress={onOpen}>
<span className="sr-only">Delete</span>
<TrashIcon className="w-4" />
</Button>
</Tooltip>
<Modal isOpen={isOpen} onOpenChange={onOpenChange} placement='top' classNames={{base:"bg-white"}}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">Delete Ad Group</ModalHeader>
<ModalBody>
<p>
Do you really want to delete this ad group?
</p>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose} className='flex h-10 items-center rounded-lg bg-gray-100 px-4 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-200'>
No
</Button>
<Button color="primary" onPress={()=>deleteAdHandler(id)} className='flex h-10 items-center rounded-lg bg-blue-600 px-4 text-sm font-medium text-white transition-colors hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600'>
Yes
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</form>
);
}
Loading
Loading