-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add review form stars, comments and upload image
- Loading branch information
1 parent
d20c447
commit 95f0d57
Showing
12 changed files
with
905 additions
and
121 deletions.
There are no files selected for viewing
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,133 @@ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-return */ | ||
import dynamic from 'next/dynamic' | ||
import type { FC} from 'react'; | ||
import { useEffect, useState } from 'react' | ||
const Select = dynamic(() => import("react-select"), { | ||
ssr: true, | ||
}) | ||
|
||
type State = { | ||
administrative_division: string; | ||
state: string | ||
capital: string, | ||
royal_capital: string, | ||
population: number, | ||
total_area: number, | ||
licence_plate_prefix: string, | ||
phone_area_code: string, | ||
abbreviation: string, | ||
ISO: string, | ||
FIPS: string, | ||
HDI: number, | ||
region: string, | ||
head_of_state: string, | ||
head_of_goverment: string | ||
} | ||
|
||
export type AddSurauFormProps = { | ||
open: boolean, | ||
setOpen: (open: boolean) => void | ||
} | ||
|
||
const AddSurauForm: FC<AddSurauFormProps> = ({ open, setOpen }) => { | ||
|
||
const [state, setState] = useState<State[]>([]); | ||
|
||
useEffect(() => { | ||
void fetch("https://jianliew.me/malaysia-api/state/v1/all.json") | ||
.then(res => res.json()) | ||
.then(data => { | ||
console.log(data) | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
setState(data) | ||
}) | ||
}, []) | ||
|
||
return ( | ||
<> | ||
<div className=""> | ||
<div className="md:grid md:grid-cols-2 md:gap-6"> | ||
<div className="md:col-span-1"> | ||
<div className="px-4 sm:px-0"> | ||
<h3 className="text-lg font-medium leading-6 text-gray-900">Add surau</h3> | ||
<p className="mt-1 text-gray-600 text-xs italic"> | ||
Help us to add surau if it is not in the list. | ||
</p> | ||
</div> | ||
</div> | ||
<div className="mt-4 md:col-span-2 md:mt-0"> | ||
<form action="#" method="POST"> | ||
<div className="shadow sm:overflow-hidden sm:rounded-md"> | ||
<div className="space-y-6 bg-white px-4 py-5 sm:p-6"> | ||
<div className="grid grid-cols-3 gap-6"> | ||
<div className="col-span-3 sm:col-span-2"> | ||
<label htmlFor="surau-name" className="block text-sm font-medium text-gray-700"> | ||
Surau Name | ||
</label> | ||
<div className="mt-1 flex rounded-md shadow-sm"> | ||
<input | ||
type="text" | ||
name="surau-name" | ||
id="surau-name" | ||
className="block w-full flex-1 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" | ||
placeholder="" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="grid grid-cols-3 gap-6"> | ||
<div className="col-span-2 sm:col-span-2"> | ||
<label htmlFor="surau-name" className="block text-sm font-medium text-gray-700"> | ||
State | ||
</label> | ||
<div className="mt-1 block rounded-md shadow-sm w-full"> | ||
<Select | ||
options={state} | ||
getOptionLabel={(option: any) => option.state} | ||
getOptionValue={(option: any) => option.state} | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
<div> | ||
<label htmlFor="about" className="block text-sm font-medium text-gray-700"> | ||
Direction / guide | ||
</label> | ||
<div className="mt-1"> | ||
<textarea | ||
id="about" | ||
name="about" | ||
rows={3} | ||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" | ||
defaultValue={''} | ||
/> | ||
</div> | ||
<p className="mt-2 text-sm text-gray-500"> | ||
Brief direction or guide to the surau. eg. near to the mosque, near to the shop lot, etc. | ||
</p> | ||
</div> | ||
|
||
|
||
</div> | ||
<div className="bg-gray-50 px-4 py-3 text-right sm:px-6 flex flex-row items-end justify-end gap-2"> | ||
<button | ||
type="submit" | ||
className=" justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" | ||
> | ||
Save | ||
</button> | ||
<div className="mb-2 font-light underline" onClick={() => setOpen(false)}>Close</div> | ||
</div> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
|
||
</> | ||
) | ||
} | ||
|
||
export default AddSurauForm; |
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,50 @@ | ||
import type { FC} from 'react'; | ||
import { Fragment, useState } from 'react' | ||
import { Dialog, Transition } from '@headlessui/react' | ||
import { CheckIcon } from '@heroicons/react/24/outline' | ||
import type { AddSurauFormProps } from './AddSurauForm'; | ||
import AddSurauForm from './AddSurauForm' | ||
|
||
type AddSurauFormModalProps = AddSurauFormProps | ||
|
||
const AddSurauFormModal: FC<AddSurauFormModalProps> = ({open, setOpen}) => { | ||
return ( | ||
<Transition.Root show={open} as={Fragment}> | ||
<Dialog as="div" className="relative z-10" onClose={setOpen}> | ||
<Transition.Child | ||
as={Fragment} | ||
enter="ease-out duration-300" | ||
enterFrom="opacity-0" | ||
enterTo="opacity-100" | ||
leave="ease-in duration-200" | ||
leaveFrom="opacity-100" | ||
leaveTo="opacity-0" | ||
> | ||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> | ||
</Transition.Child> | ||
|
||
<div className="fixed inset-0 z-10 overflow-y-auto"> | ||
<div className="flex items-end justify-center p-4 text-center sm:items-center sm:p-0"> | ||
<Transition.Child | ||
as={Fragment} | ||
enter="ease-out duration-300" | ||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" | ||
enterTo="opacity-100 translate-y-0 sm:scale-100" | ||
leave="ease-in duration-200" | ||
leaveFrom="opacity-100 translate-y-0 sm:scale-100" | ||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" | ||
> | ||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-4xl sm:p-6"> | ||
<div> | ||
<AddSurauForm setOpen={setOpen} open /> | ||
</div> | ||
</Dialog.Panel> | ||
</Transition.Child> | ||
</div> | ||
</div> | ||
</Dialog> | ||
</Transition.Root> | ||
) | ||
} | ||
|
||
export default AddSurauFormModal; |
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,144 @@ | ||
import { StarIcon } from "@heroicons/react/20/solid"; | ||
import Image from "next/image"; | ||
import { FC, useEffect } from "react"; | ||
import { useState } from "react" | ||
|
||
|
||
export type ReviewSurauFormProps = { | ||
open: boolean, | ||
setOpen: (open: boolean) => void | ||
surauName: string | ||
} | ||
|
||
type ImagePreviews = { | ||
url: string, | ||
} | ||
const ReviewSurauForm: FC<ReviewSurauFormProps> = ({ open, setOpen, surauName }) => { | ||
|
||
const [rating, setRating] = useState(0); | ||
const [imagePreviews, setImagePreviews] = useState<ImagePreviews[]>(); | ||
|
||
const handleRatingChange = (newRating: number) => { | ||
setRating(newRating); | ||
}; | ||
|
||
const renderStars = () => { | ||
const stars = []; | ||
for (let i = 1; i <= 5; i++) { | ||
stars.push( | ||
<StarIcon | ||
key={i} | ||
className={`w-6 h-6 ${i <= rating ? "text-yellow-500" : "text-gray-400" | ||
} cursor-pointer`} | ||
onClick={() => handleRatingChange(i)} | ||
/> | ||
); | ||
} | ||
|
||
return stars; | ||
}; | ||
|
||
const selectImages = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
const images: ImagePreviews[] = []; | ||
|
||
if (e.target.files === null) return; | ||
|
||
for (let i = 0; i < e.target.files.length; i++) { | ||
images.push(URL.createObjectURL(e.target.files[i] as Blob) as unknown as ImagePreviews); | ||
} | ||
|
||
setImagePreviews(images); | ||
} | ||
|
||
return ( | ||
<div className=""> | ||
<div className="md:grid md:grid-cols-2 md:gap-6"> | ||
<div className="md:col-span-1"> | ||
<div className="px-4 sm:px-0"> | ||
<h3 className="text-lg font-medium leading-6 text-gray-900">Review</h3> | ||
<p className="mt-1 text-gray-600 text-xs italic"> | ||
Review this {surauName} surau inshaAllah | ||
</p> | ||
</div> | ||
</div> | ||
<div className="mt-4 md:col-span-2 md:mt-0"> | ||
|
||
<div className="shadow sm:overflow-hidden sm:rounded-md"> | ||
<div className="flex items-center justify-center pt-4"> | ||
{renderStars()} | ||
</div> | ||
<div className="space-y-6 bg-white px-4 py-5 sm:p-6"> | ||
<div> | ||
<label htmlFor="about" className="block text-sm font-medium text-gray-700"> | ||
</label> | ||
<div className="mt-1"> | ||
<textarea | ||
id="about" | ||
name="about" | ||
rows={3} | ||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" | ||
defaultValue={''} | ||
/> | ||
</div> | ||
<p className="mt-2 text-sm text-gray-500"> | ||
Add your honest review about this surau and also any improvement that can be made. | ||
</p> | ||
</div> | ||
<div className="mt-1 sm:col-span-2 sm:mt-0"> | ||
<div className="flex max-w-lg justify-center rounded-md border-2 border-dashed border-gray-300 px-6 pt-5 pb-6"> | ||
<div className="space-y-1 text-center"> | ||
<svg | ||
className="mx-auto h-12 w-12 text-gray-400" | ||
stroke="currentColor" | ||
fill="none" | ||
viewBox="0 0 48 48" | ||
aria-hidden="true" | ||
> | ||
<path | ||
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" | ||
strokeWidth={2} | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
/> | ||
</svg> | ||
<div className="flex text-sm text-gray-600"> | ||
<label | ||
className="relative cursor-pointer rounded-md bg-white font-medium text-indigo-600 focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 hover:text-indigo-500" | ||
> | ||
<span>Upload a file</span> | ||
<input onChange={selectImages} name="file-upload" type="file" className="sr-only" multiple accept="image/*" /> | ||
</label> | ||
<p className="pl-1">or drag and drop</p> | ||
</div> | ||
<p className="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="flex flex-row gap-2"> | ||
{imagePreviews ?( | ||
imagePreviews.map((imagePreview, index) => ( | ||
<div key={index} className=""> | ||
<Image src={imagePreview as unknown as string} alt="xsd" height={250} width={250} /> | ||
</div> | ||
) | ||
)) : null} | ||
</div> | ||
</div> | ||
<div className="bg-gray-50 px-4 py-3 text-right sm:px-6 flex flex-row items-end justify-end gap-2"> | ||
<button | ||
className=" justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" | ||
> | ||
Submit Review | ||
</button> | ||
<div className="mb-2 font-light underline" onClick={() => setOpen(false)}>Close</div> | ||
</div> | ||
</div> | ||
|
||
</div> | ||
</div> | ||
</div> | ||
) | ||
|
||
} | ||
|
||
export default ReviewSurauForm |
Oops, something went wrong.
95f0d57
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ratemysurau – ./
ratemysurau-git-main-farhan-helmy.vercel.app
www.ratemysurau.com
ratemysurau.vercel.app
ratemysurau-farhan-helmy.vercel.app
ratemysurau.com