Skip to content

Commit

Permalink
feat:UI completions for channels
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielEmmanuel1 committed Oct 23, 2024
1 parent 6b36db7 commit 6079a2b
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 232 deletions.
5 changes: 5 additions & 0 deletions src/assets/logoAnimate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 19 additions & 15 deletions src/components/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { forwardRef, useContext, useState } from "react";
import React, { forwardRef, useContext } from "react";
import { AuthContext } from "../context/AuthContext";
import LogoWithText from "../assets/logo_with_text.png";
import SideBarData from "../data/sidebar.json";
import { Link } from "react-router-dom";
import { NavLink } from "react-router-dom";

interface SideBarProps {
isOpen: boolean;
Expand All @@ -18,18 +18,21 @@ interface User {
const SideBar = forwardRef<HTMLDivElement, SideBarProps & React.HTMLAttributes<HTMLDivElement>>((props, ref) => {
const { userData } = useContext(AuthContext);

// Optional fallback for userData
// If userData is not available, show the loading animation
if (!userData) {
return <div>Loading...</div>; // Customize as needed
return (
<div className="flex justify-center items-center w-full h-screen">
<img
src={LogoWithText}
alt="logo"
className="animate-breathing w-[120px]"
/>
</div>

);
}

const user = userData.user as unknown as User;
// eslint-disable-next-line react-hooks/rules-of-hooks
const [activeTab, setActiveTab] = useState<string>("Home");

const handleActiveTab = (tab: string) => {
setActiveTab(tab);
};

return (
<div
Expand All @@ -52,13 +55,14 @@ const SideBar = forwardRef<HTMLDivElement, SideBarProps & React.HTMLAttributes<H
<span className="text-xl font-semibold">{nav.title}</span>
</div>
) : (
<Link
<NavLink
to={nav.url}
className={`${activeTab === nav.title ? 'text-[#03CF79]' : 'text-black'}`}
onClick={() => handleActiveTab(nav.title)}
className={({ isActive }) =>
isActive ? 'text-[#03CF79]' : 'text-black'
}
>
<span className="text-xl font-semibold">{nav.title}</span>
</Link>
</NavLink>
)}
</li>
))}
Expand Down Expand Up @@ -92,4 +96,4 @@ const SideBar = forwardRef<HTMLDivElement, SideBarProps & React.HTMLAttributes<H
);
});

export default SideBar;
export default SideBar;
184 changes: 101 additions & 83 deletions src/components/modals/channels/Creation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Checkbox } from "@/components/ui/checkbox"
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { cn } from "@/lib/utils"
import toast, {Toaster} from 'react-hot-toast'
import toast, { Toaster } from 'react-hot-toast'
import { AuthContext } from '@/context/AuthContext'
import LogoWithText from "../../../assets/logoAnimate.svg";
import apiUrl from '@/data/axios'
import axios from 'axios'
import { HomeDataResponse } from '@/components/pages/dashboard/home/response'
Expand All @@ -34,22 +35,22 @@ interface FormData {
}

interface CreateChannelModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
data: HomeDataResponse | null;
open: boolean;
onOpenChange: (open: boolean) => void;
data: HomeDataResponse | null;
}

interface ChannelCreatedResponse {
id: number|string;
channel_id: number|string;
admin_id: number|string;
name: string;
type: string;
description: string;
id: number | string;
channel_id: number | string;
admin_id: number | string;
name: string;
type: string;
description: string;
}

export default function CreateChannelModal({ open, onOpenChange, data }: CreateChannelModalProps) {
const {userData} = useContext(AuthContext)
const { userData } = useContext(AuthContext)
const [step, setStep] = useState(1)
const [formData, setFormData] = useState<FormData>({
type: "Public",
Expand Down Expand Up @@ -115,7 +116,7 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
if (step < 5) {
setStep(step + 1)
} else {
onOpenChange(false)
onOpenChange(false)
}
}

Expand All @@ -133,22 +134,22 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC

const handleProceedWithAdmin = () => {
if (step === 4) {
handleSubmit()
setIsLoading(false)
setStep(step + 1)
handleSubmit()
setIsLoading(false)
setStep(step + 1)
}
}

const handleSubmit = async () => {
setIsLoading(true);
let ch_id = '';
if (data?.channels_managed !== null){
data?.channels_managed.forEach((id, index) => {
if (index === 0) {
ch_id = id.toString()
}
return;
});
if (data?.channels_managed !== null) {
data?.channels_managed.forEach((id, index) => {
if (index === 0) {
ch_id = id.toString()
}
return;
});
}

// Prepare form data
Expand All @@ -168,14 +169,15 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
form.append('type', formData.type);

try {
const API_URL = apiUrl("production");
const API_URL = apiUrl("production");
const response = await axios.post(`${API_URL}/api/subchannel`, form, {
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
'Authorization': `Bearer ${userData?.token}`
// No need for 'Content-Type' here, fetch automatically sets it for FormData
}});
}
});

if (response.status !== 201) {
toast.error('Failed')
Expand All @@ -188,29 +190,29 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
toast.success('Channel created!')
setIsSkipped(true)
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
// Server responded with a status other than 2xx
toast.error('process failed')
console.error("Process failed. Please try again.");
} else if (error.request) {
// No response was received from the server
toast.error('network error')
console.error(
"Network error. Please check your connection and try again."
);
} else {
// Something else happened while setting up the request
toast.error('unexpected error')
console.error("An unexpected error occurred. Please try again.");
}
} else {
// Non-Axios error
toast.error('something went wrong')
console.error("An unexpected error occurred. Please try again.");
}
console.error("channel error:", error);
setStep(3)
if (axios.isAxiosError(error)) {
if (error.response) {
// Server responded with a status other than 2xx
toast.error('process failed')
console.error("Process failed. Please try again.");
} else if (error.request) {
// No response was received from the server
toast.error('network error')
console.error(
"Network error. Please check your connection and try again."
);
} else {
// Something else happened while setting up the request
toast.error('unexpected error')
console.error("An unexpected error occurred. Please try again.");
}
} else {
// Non-Axios error
toast.error('something went wrong')
console.error("An unexpected error occurred. Please try again.");
}
console.error("channel error:", error);
setStep(3)
} finally {
setIsLoading(false);
}
Expand All @@ -219,22 +221,23 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
const handleAddAdmin = async () => {
setIsLoading(true);
const adminInvite = {
channel_id: isResponse.channel_id,
sub_channel_id: isResponse.id,
user_id: isResponse.admin_id,
email_invited: formData.adminEmail,
email_body: formData.adminInstructions
channel_id: isResponse.channel_id,
sub_channel_id: isResponse.id,
user_id: isResponse.admin_id,
email_invited: formData.adminEmail,
email_body: formData.adminInstructions
}

try {
const API_URL = apiUrl("production");
const API_URL = apiUrl("production");
const response = await axios.post(`${API_URL}/api/email/invite/user`, adminInvite, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${userData?.token}`
// No need for 'Content-Type' here, fetch automatically sets it for FormData
}});
}
});

if (response.status !== 200) {
toast.error('Failed to add')
Expand All @@ -245,28 +248,28 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
console.log(channel);
toast.success('Admin Invited!')
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
// Server responded with a status other than 2xx
toast.error('process failed')
console.error("Process failed. Please try again.");
} else if (error.request) {
// No response was received from the server
toast.error('network error')
console.error(
"Network error. Please check your connection and try again."
);
} else {
// Something else happened while setting up the request
toast.error('unexpected error')
console.error("An unexpected error occurred. Please try again.");
}
} else {
// Non-Axios error
toast.error('something went wrong')
console.error("An unexpected error occurred. Please try again.");
}
console.error("admin add error:", error);
if (axios.isAxiosError(error)) {
if (error.response) {
// Server responded with a status other than 2xx
toast.error('process failed')
console.error("Process failed. Please try again.");
} else if (error.request) {
// No response was received from the server
toast.error('network error')
console.error(
"Network error. Please check your connection and try again."
);
} else {
// Something else happened while setting up the request
toast.error('unexpected error')
console.error("An unexpected error occurred. Please try again.");
}
} else {
// Non-Axios error
toast.error('something went wrong')
console.error("An unexpected error occurred. Please try again.");
}
console.error("admin add error:", error);
} finally {
setIsLoading(false);
setTimeout(() => {
Expand All @@ -277,7 +280,7 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
};

const renderStepContent = () => {
switch(step) {
switch (step) {
case 1:
return (
<RadioGroup
Expand All @@ -301,9 +304,9 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
className="text-base font-semibold capitalize cursor-pointer"
>
{type}
<p className="text-xs sm:text-sm text-gray-500">
{typeDesc[index]}
</p>
<p className="text-xs sm:text-sm text-gray-500">
{typeDesc[index]}
</p>
</Label>
</div>
<RadioGroupItem
Expand All @@ -330,7 +333,7 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
<Label>Profile Image</Label>
<div className="bg-[#DDF6EC] text-xs sm:text-sm font-medium border border-[#03CF79] p-4 text-center rounded-md">
<Label htmlFor='profileImage' className='w-full'>Tap to select 1280x1280 (recommended)</Label>
<input type='file' name='profileImage' id='profileImage' className='sr-only'/>
<input type='file' name='profileImage' id='profileImage' className='sr-only' />
</div>
</div>
<div>
Expand Down Expand Up @@ -431,7 +434,7 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
<DialogTitle className="text-center">
<span>New channel</span>
</DialogTitle>
<span className="text-sm text-muted-foreground text-right">{step}/5</span>
<span className="text-sm text-muted-foreground text-right">{step}/5</span>
</DialogHeader>
<div className="pb-4">
{renderStepContent()}
Expand All @@ -441,7 +444,22 @@ export default function CreateChannelModal({ open, onOpenChange, data }: CreateC
<Button onClick={() => {
step > 4 ? handleAddAdmin() : step > 1 && step === 4 ? handleProceedWithAdmin() : handleNext()
}} disabled={isLoading} className='bg-[#03CF79]'>
{isLoading ? "Loading..." : step < 4 ? "Next" : step === 4 ? "Proceed" : "Add admin"}
{isLoading ? (
<div className="flex justify-center items-center w-full h-screen">
<img
src={LogoWithText}
alt="logo"
className="animate-breathing w-[150.86px]"
/>
</div>
) : step < 4 ? (
"Next"
) : step === 4 ? (
"Proceed"
) : (
"Add admin"
)}

</Button>
</DialogFooter>
</DialogContent>
Expand Down
Loading

0 comments on commit 6079a2b

Please sign in to comment.