generated from codersforcauses/django-nextjs-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
089fb12
commit 8b13a8e
Showing
13 changed files
with
736 additions
and
66 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { profile } from "console"; | ||
import Image from "next/image"; | ||
import { ChangeEvent, useState } from "react"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { HTMLTextTargetElement, SingleLineInput } from "@/components/ui/inputs"; | ||
|
||
interface AvaProps { | ||
profilePhoto: File | null; | ||
currentStep: number; | ||
setCurrentStep: React.Dispatch<React.SetStateAction<number>>; | ||
setProfilePhoto: React.Dispatch<React.SetStateAction<string | undefined>>; | ||
setSubmit: React.Dispatch<React.SetStateAction<boolean>>; | ||
client: string; | ||
setTitle: React.Dispatch<React.SetStateAction<string>>; | ||
} | ||
|
||
export const Ava: React.FC<AvaProps> = ({ | ||
currentStep, | ||
setCurrentStep, | ||
profilePhoto, | ||
setProfilePhoto, | ||
client, | ||
setSubmit, | ||
setTitle, | ||
}) => { | ||
const [buttonVariant, setButtonVariant] = useState("inactive"); | ||
const [showOptions, setShowOptions] = useState(false); | ||
const [inputType, setInputType] = useState<"file" | "camera">("file"); | ||
|
||
const handleAvatarChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
const file = e.target.files?.[0]; | ||
if (file) { | ||
const reader = new FileReader(); | ||
reader.onload = () => { | ||
setProfilePhoto(reader.result as string); | ||
}; | ||
reader.readAsDataURL(file); | ||
setButtonVariant("default"); | ||
} | ||
}; | ||
|
||
const handleCameraCapture = async () => { | ||
try { | ||
const stream = await navigator.mediaDevices.getUserMedia({ video: true }); | ||
const video = document.createElement("video"); | ||
video.srcObject = stream; | ||
video.play(); | ||
|
||
return new Promise<string>((resolve, reject) => { | ||
video.onloadedmetadata = () => { | ||
video.width = video.videoWidth; | ||
video.height = video.videoHeight; | ||
|
||
const canvas = document.createElement("canvas"); | ||
canvas.width = video.videoWidth; | ||
canvas.height = video.videoHeight; | ||
|
||
const context = canvas.getContext("2d"); | ||
context?.drawImage(video, 0, 0); | ||
|
||
canvas.toDataURL( | ||
"image/*", | ||
(dataUrl: string | PromiseLike<string>) => { | ||
resolve(dataUrl); | ||
stream.getTracks().forEach((track) => track.stop()); | ||
}, | ||
); | ||
}; | ||
}); | ||
} catch (error) { | ||
console.error("Error accessing camera", error); | ||
} | ||
}; | ||
|
||
const handleChooseOption = (type: "file" | "camera") => { | ||
setInputType(type); | ||
if (type === "file") { | ||
document.getElementById("fileInput")?.click(); | ||
} else if (type === "camera") { | ||
handleCameraCapture().then((dataUrl) => { | ||
setProfilePhoto(dataUrl); | ||
setShowOptions(false); | ||
}); | ||
} | ||
}; | ||
|
||
const buttonInputVariant = | ||
buttonVariant === "default" ? "default" : "inactive"; | ||
|
||
const buttonEffect = () => { | ||
if (client === "Poster") { | ||
setSubmit(true); | ||
} else { | ||
setCurrentStep(currentStep + 1); | ||
setTitle("Your Bio"); | ||
} | ||
}; | ||
|
||
return ( | ||
<div> | ||
<div className="absolute left-0 right-0 top-[150px] flex h-[120px] flex-col gap-3 p-[1px_0] px-4"> | ||
<span className="body text-primary"> | ||
Upload a profile photo so others can recognise you! | ||
</span> | ||
{/*Avatar Uploader*/} | ||
<div className="flex items-center justify-center"> | ||
<div className="relative h-[136px] w-[136px]"> | ||
{profilePhoto ? ( | ||
<Image | ||
src={profilePhoto || "/default-profile.svg"} | ||
alt="Avatar" | ||
layout="fill" | ||
objectFit="cover" | ||
className="rounded-full border-2 border-penni-grey-inactive" | ||
/> | ||
) : ( | ||
<div className="flex h-full w-full items-center justify-center rounded-full border-2 border-penni-grey-inactive bg-penni-grey-inactive"> | ||
<span className="bold text-penni-grey-inactive">No image</span> | ||
</div> | ||
)} | ||
<input | ||
type="file" | ||
id="fileInput" | ||
accept="image/*" | ||
onChange={handleAvatarChange} | ||
className="absolute inset-0 cursor-pointer opacity-0" | ||
/> | ||
<button | ||
className="absolute inset-0 cursor-pointer opacity-0" | ||
onClick={() => setShowOptions(true)} | ||
> | ||
Upload | ||
</button> | ||
</div> | ||
|
||
{showOptions && ( | ||
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black bg-opacity-50 transition-transform"> | ||
<div className="duration-800 animate-slide-up w-full space-y-1 rounded-t-lg p-4 ease-in-out"> | ||
<Button | ||
className="flex h-[56px] w-full px-4" | ||
variant="filecard" | ||
onClick={() => handleChooseOption("file")} | ||
> | ||
Choose from Gallery | ||
</Button> | ||
<Button | ||
className="flex h-[56px] w-full px-4" | ||
variant="filecard" | ||
onClick={() => handleChooseOption("camera")} | ||
> | ||
Take a Photo | ||
</Button> | ||
<Button | ||
className="mt-1 flex h-[56px] w-full px-4" | ||
variant="filecard" | ||
onClick={() => setShowOptions(false)} | ||
> | ||
Cancel | ||
</Button> | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
<div className="absolute left-0 right-0 top-[574px] flex flex-col gap-3 p-[1px_0] px-4"> | ||
<Button | ||
className="flex h-[56px] w-full px-4 pb-4" | ||
variant={buttonInputVariant} | ||
onClick={buttonEffect} | ||
disabled={buttonInputVariant === "inactive"} | ||
> | ||
Continue | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { ChangeEvent, useState } from "react"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { | ||
HTMLTextTargetElement, | ||
ParagraphInput, | ||
SingleLineInput, | ||
} from "@/components/ui/inputs"; | ||
|
||
interface BioProps { | ||
setSubmit: React.Dispatch<React.SetStateAction<boolean>>; | ||
bio: string; | ||
setBio: React.Dispatch<React.SetStateAction<string>>; | ||
} | ||
|
||
export const Bio: React.FC<BioProps> = ({ setBio, setSubmit, bio }) => { | ||
const [buttonVariant, setButtonVariant] = useState("inactive"); | ||
|
||
const handleBioChange = (e: ChangeEvent<HTMLTextTargetElement>) => { | ||
const value = e.target.value; | ||
setBio(value); | ||
setButtonVariant(value ? "default" : "inactive"); | ||
}; | ||
|
||
const buttonInputVariant = | ||
buttonVariant === "default" ? "default" : "inactive"; | ||
return ( | ||
<div> | ||
<div className="absolute left-0 right-0 top-[150px] flex h-[120px] flex-col gap-3 p-[1px_0] px-4"> | ||
<span className="body text-primary"> | ||
Tell us a bit about yourself, like what you are best at and your | ||
background | ||
</span> | ||
<div className="w-full"> | ||
<ParagraphInput | ||
name="Bio" | ||
onChange={handleBioChange} | ||
value={bio} | ||
label="What are you best at?" | ||
placeholder="Ever since I was little, I've always been passionate about not starving to death." | ||
/> | ||
</div> | ||
</div> | ||
<div className="absolute left-0 right-0 top-[574px] flex flex-col gap-3 p-[1px_0] px-4"> | ||
<Button | ||
className="flex h-[56px] w-full px-4 pb-4" | ||
variant={buttonInputVariant} | ||
onClick={() => setSubmit(true)} | ||
disabled={buttonInputVariant === "inactive"} | ||
> | ||
Continue | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { AxiosError } from "axios"; | ||
|
||
import api from "@/lib/api"; | ||
|
||
type AxiosCustomError = AxiosError<{ | ||
message: string; | ||
}>; | ||
|
||
export const checkUnique = async (input: string): Promise<boolean> => { | ||
try { | ||
// this check logic should be revised, it will be a mess as data amount grows. | ||
const response = await api.get("app/users/"); | ||
// axios automatically parses the response as JSON if the content type is application/json | ||
const data = response.data; | ||
for (const profile of data) { | ||
if (profile.full_name === input) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} catch (error) { | ||
const axiosError = error as AxiosCustomError; | ||
if (axiosError.response) { | ||
// The request was made and the server responded with a status code | ||
// that falls out of the range of 2xx | ||
throw new Error( | ||
`Server responded with status: ${axiosError.response.status}`, | ||
); | ||
} else if (axiosError.request) { | ||
// The request was made but no response was received | ||
throw new Error("No response received from server"); | ||
} else { | ||
// Something happened in setting up the request that triggered an Error | ||
throw new Error(`Error in setting up request: ${axiosError.message}`); | ||
} | ||
} | ||
}; |
Oops, something went wrong.