unknown
1 month ago
21 changed files with 688 additions and 106 deletions
-
22package-lock.json
-
1package.json
-
11public/assets/images/Frame 26952.svg
-
3public/assets/images/HomeIcon.svg
-
6public/assets/images/dots-vertical-svgrepo-com.svg
-
BINpublic/assets/images/jamkaran.png
-
5public/assets/images/search-alt-svgrepo-com.svg
-
4public/assets/images/🦆 icon _play_.svg
-
2src/api/http.tsx
-
18src/components/layout/header.tsx
-
18src/components/layout/sidebar.tsx
-
82src/components/sidebar/list.tsx
-
113src/components/sidebar/tabs.tsx
-
39src/components/ui/footer-sticky.tsx
-
127src/components/ui/search-duas.tsx
-
2src/components/utils/colorize-vowels.tsx
-
48src/components/utils/hooks/local-storage.tsx
-
9src/pages/_app.tsx
-
38src/pages/about.tsx
-
215src/pages/duas/[slug].tsx
-
3src/pages/index.tsx
@ -0,0 +1,11 @@ |
|||||
|
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<g clip-path="url(#clip0_5011_5546)"> |
||||
|
<path d="M14 24.5C11.2152 24.5 8.54451 23.3938 6.57538 21.4246C4.60625 19.4555 3.5 16.7848 3.5 14C3.5 11.2152 4.60625 8.54451 6.57538 6.57538C8.54451 4.60625 11.2152 3.5 14 3.5C16.7848 3.5 19.4555 4.60625 21.4246 6.57538C23.3938 8.54451 24.5 11.2152 24.5 14C24.5 16.7848 23.3938 19.4555 21.4246 21.4246C19.4555 23.3938 16.7848 24.5 14 24.5ZM14 26C17.1826 26 20.2348 24.7357 22.4853 22.4853C24.7357 20.2348 26 17.1826 26 14C26 10.8174 24.7357 7.76516 22.4853 5.51472C20.2348 3.26428 17.1826 2 14 2C10.8174 2 7.76516 3.26428 5.51472 5.51472C3.26428 7.76516 2 10.8174 2 14C2 17.1826 3.26428 20.2348 5.51472 22.4853C7.76516 24.7357 10.8174 26 14 26Z" fill="#4D4D4D"/> |
||||
|
<path d="M15.3949 11.882L11.9599 12.3125L11.8369 12.8825L12.5119 13.007C12.9529 13.112 13.0399 13.271 12.9439 13.7105L11.8369 18.9125C11.5459 20.258 11.9944 20.891 13.0489 20.891C13.8664 20.891 14.8159 20.513 15.2464 19.994L15.3784 19.37C15.0784 19.634 14.6404 19.739 14.3494 19.739C13.9369 19.739 13.7869 19.4495 13.8934 18.9395L15.3949 11.882ZM15.4999 8.75C15.4999 9.14782 15.3419 9.52936 15.0606 9.81066C14.7793 10.092 14.3978 10.25 13.9999 10.25C13.6021 10.25 13.2206 10.092 12.9393 9.81066C12.658 9.52936 12.4999 9.14782 12.4999 8.75C12.4999 8.35218 12.658 7.97064 12.9393 7.68934C13.2206 7.40804 13.6021 7.25 13.9999 7.25C14.3978 7.25 14.7793 7.40804 15.0606 7.68934C15.3419 7.97064 15.4999 8.35218 15.4999 8.75Z" fill="#4D4D4D"/> |
||||
|
</g> |
||||
|
<defs> |
||||
|
<clipPath id="clip0_5011_5546"> |
||||
|
<rect width="24" height="24" fill="white" transform="translate(2 2)"/> |
||||
|
</clipPath> |
||||
|
</defs> |
||||
|
</svg> |
@ -0,0 +1,3 @@ |
|||||
|
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<path d="M8.38889 0.59753C8.83974 0.211641 9.41038 0 10 0C10.5896 0 11.1603 0.211641 11.6111 0.59753L19.1111 7.0238C19.6756 7.50563 20 8.2154 20 8.96466V20.0253C20 20.549 19.7951 21.0513 19.4305 21.4216C19.0658 21.792 18.5713 22 18.0556 22H14.7222C14.2065 22 13.7119 21.792 13.3473 21.4216C12.9826 21.0513 12.7778 20.549 12.7778 20.0253V14.3833C12.7778 14.1588 12.69 13.9435 12.5337 13.7848C12.3774 13.6261 12.1655 13.537 11.9444 13.537H8.05556C7.83454 13.537 7.62258 13.6261 7.4663 13.7848C7.31002 13.9435 7.22222 14.1588 7.22222 14.3833V20.0253C7.22222 20.549 7.01736 21.0513 6.65271 21.4216C6.28805 21.792 5.79348 22 5.27778 22H1.94444C1.6891 22 1.43625 21.9489 1.20034 21.8497C0.964427 21.7504 0.750073 21.605 0.569515 21.4216C0.388956 21.2383 0.24573 21.0206 0.148012 20.781C0.0502946 20.5414 0 20.2846 0 20.0253V8.96353C0 8.2154 0.324444 7.50563 0.888889 7.02267L8.38889 0.59753Z" fill="#8B8B8B"/> |
||||
|
</svg> |
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> |
||||
|
<svg width="28px" height="28px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<path d="M8 12C9.10457 12 10 12.8954 10 14C10 15.1046 9.10457 16 8 16C6.89543 16 6 15.1046 6 14C6 12.8954 6.89543 12 8 12Z" fill="#4D4D4D"/> |
||||
|
<path d="M8 6C9.10457 6 10 6.89543 10 8C10 9.10457 9.10457 10 8 10C6.89543 10 6 9.10457 6 8C6 6.89543 6.89543 6 8 6Z" fill="#4D4D4D"/> |
||||
|
<path d="M10 2C10 0.89543 9.10457 -4.82823e-08 8 0C6.89543 4.82823e-08 6 0.895431 6 2C6 3.10457 6.89543 4 8 4C9.10457 4 10 3.10457 10 2Z" fill="#4D4D4D"/> |
||||
|
</svg> |
After Width: 661 | Height: 371 | Size: 370 KiB |
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> |
||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<path d="M17 17L21 21" stroke="#323232" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> |
||||
|
<path d="M19 11C19 15.4183 15.4183 19 11 19C6.58172 19 3 15.4183 3 11C3 6.58172 6.58172 3 11 3C15.4183 3 19 6.58172 19 11Z" stroke="#4D4D4D" stroke-width="2"/> |
||||
|
</svg> |
@ -0,0 +1,4 @@ |
|||||
|
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<path d="M13.273 10.4996L9.39478 13.1758V7.82411L13.273 10.4996Z" fill="#F4846F" stroke="#F4846F" stroke-width="1.58643"/> |
||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 2.29545C8.32402 2.29545 6.23716 3.15986 4.69851 4.69851C3.15986 6.23716 2.29545 8.32402 2.29545 10.5C2.29545 12.676 3.15986 14.7628 4.69851 16.3015C6.23716 17.8401 8.32402 18.7045 10.5 18.7045C12.676 18.7045 14.7628 17.8401 16.3015 16.3015C17.8401 14.7628 18.7045 12.676 18.7045 10.5C18.7045 8.32402 17.8401 6.23716 16.3015 4.69851C14.7628 3.15986 12.676 2.29545 10.5 2.29545ZM1 10.5C1 5.25341 5.25341 1 10.5 1C15.7466 1 20 5.25341 20 10.5C20 15.7466 15.7466 20 10.5 20C5.25341 20 1 15.7466 1 10.5Z" fill="#F4846F" stroke="#F4846F" stroke-width="0.38"/> |
||||
|
</svg> |
@ -0,0 +1,39 @@ |
|||||
|
import Home from "../../../public/assets/images/HomeIcon.svg"; |
||||
|
import About from "../../../public/assets/images/Frame 26952.svg"; |
||||
|
import Search from "../../../public/assets/images/search-alt-svgrepo-com.svg"; |
||||
|
import Menue from "../../../public/assets/images/dots-vertical-svgrepo-com.svg"; |
||||
|
import Image from "next/image"; |
||||
|
|
||||
|
const FooterSticky = () => { |
||||
|
const Navigations = [ |
||||
|
{ |
||||
|
name: "Home", |
||||
|
icon: Home, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Search", |
||||
|
icon: Search, |
||||
|
}, |
||||
|
{ |
||||
|
name: "About Us", |
||||
|
icon: About, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Menue", |
||||
|
icon: Menue, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
return ( |
||||
|
<div className="sticky bottom-0 flex justify-between items-center lg:hidden"> |
||||
|
{Navigations.map((item) => ( |
||||
|
<div key={item.name} className="flex items-center flex-col"> |
||||
|
<Image width={28} height={28} alt={item.name} src={item.icon} /> |
||||
|
<p>{item.name}</p> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default FooterSticky; |
@ -0,0 +1,127 @@ |
|||||
|
import React, { useEffect, useState, ChangeEvent, useRef } from "react"; |
||||
|
import http from "@/api/http"; |
||||
|
import { HiMiniMagnifyingGlass } from "react-icons/hi2"; |
||||
|
|
||||
|
interface Dua { |
||||
|
id: number; |
||||
|
title: string; |
||||
|
// Add other relevant fields based on your API response
|
||||
|
} |
||||
|
|
||||
|
const SearchDuas: React.FC = () => { |
||||
|
const [value, setValue] = useState<string>(""); |
||||
|
const [results, setResults] = useState<Dua[]>([]); |
||||
|
const [isLoading, setIsLoading] = useState<boolean>(false); |
||||
|
const [error, setError] = useState<string | null>(null); |
||||
|
const [show, setShow] = useState<boolean>(false); |
||||
|
|
||||
|
// Ref for the main container
|
||||
|
const containerRef = useRef<HTMLDivElement>(null); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
// If the input is empty, reset results and do not send a request
|
||||
|
if (value.trim() === "") { |
||||
|
setResults([]); |
||||
|
setError(null); |
||||
|
setShow(false) |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Set loading state
|
||||
|
setIsLoading(true); |
||||
|
setError(null); |
||||
|
setShow(true) |
||||
|
// Implement debounce: wait for 500ms after the user stops typing
|
||||
|
const debounceTimer = setTimeout(() => { |
||||
|
// Perform the search API call with the actual user input
|
||||
|
http |
||||
|
.get(`web/mafatih-duas/?search=${encodeURIComponent(value)}`) |
||||
|
.then((res) => { |
||||
|
setResults(res.data.results); |
||||
|
setIsLoading(false); |
||||
|
// Optionally, you can handle the response here
|
||||
|
// console.log(res);
|
||||
|
}) |
||||
|
.catch((err) => { |
||||
|
console.error("Error fetching search results:", err); |
||||
|
setError("Failed to fetch search results."); |
||||
|
setIsLoading(false); |
||||
|
}); |
||||
|
}, 500); // 500ms debounce duration
|
||||
|
|
||||
|
// Cleanup function to cancel the timeout if the value changes before 500ms
|
||||
|
return () => clearTimeout(debounceTimer); |
||||
|
}, [value]); |
||||
|
|
||||
|
// Handle input changes
|
||||
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { |
||||
|
setValue(e.target.value); |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
// Function to handle clicks outside the component
|
||||
|
const handleClickOutside = (event: MouseEvent) => { |
||||
|
if ( |
||||
|
containerRef.current && |
||||
|
!containerRef.current.contains(event.target as Node) |
||||
|
) { |
||||
|
setShow(false) |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Bind the event listener
|
||||
|
document.addEventListener("mousedown", handleClickOutside); |
||||
|
|
||||
|
return () => { |
||||
|
// Unbind the event listener on cleanup
|
||||
|
document.removeEventListener("mousedown", handleClickOutside); |
||||
|
}; |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<div className="relative flex flex-col items-center" ref={containerRef}> |
||||
|
<div className="flex items-center w-64 h-11 px-4 rounded-2xl gap-3 bg-[#EBEBEB]"> |
||||
|
<label htmlFor="search-input" className="cursor-pointer"> |
||||
|
<HiMiniMagnifyingGlass size={24} /> |
||||
|
</label> |
||||
|
<input |
||||
|
onChange={handleChange} |
||||
|
id="search-input" |
||||
|
type="text" |
||||
|
value={value} |
||||
|
placeholder="Type a title or keyword to search" |
||||
|
className="text-xs bg-[#EBEBEB] w-full focus:outline-none" |
||||
|
aria-label="Search Duas" |
||||
|
onClick={()=>{setShow(true)}} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
{/* Loading Indicator */} |
||||
|
{isLoading && show &&( |
||||
|
<div className="mt-2 text-sm text-gray-600">Loading...</div> |
||||
|
)} |
||||
|
|
||||
|
{/* Error Message */} |
||||
|
{error && show && ( |
||||
|
<div className="mt-2 text-sm text-red-500">{error}</div> |
||||
|
)} |
||||
|
|
||||
|
{/* Search Results */} |
||||
|
{results.length > 0 && show && ( |
||||
|
<ul className="absolute top-14 mt-2 w-64 z-20 bg-white shadow-lg rounded-lg h-60 overflow-y-auto"> |
||||
|
{results.map((dua) => ( |
||||
|
<li |
||||
|
key={dua.id} |
||||
|
className="px-4 py-2 hover:bg-gray-100 cursor-pointer" |
||||
|
// Add onClick handler or link if needed
|
||||
|
> |
||||
|
{dua.title} |
||||
|
</li> |
||||
|
))} |
||||
|
</ul> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default SearchDuas; |
@ -0,0 +1,48 @@ |
|||||
|
import { useState, useEffect } from "react"; |
||||
|
|
||||
|
/** |
||||
|
* useLocalStorage Hook |
||||
|
* |
||||
|
* @param key - The key in localStorage |
||||
|
* @param initialValue - The initial value to use if key is not found |
||||
|
* @returns [storedValue, setValue] - The current value and a setter function |
||||
|
*/ |
||||
|
function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] { |
||||
|
const [storedValue, setStoredValue] = useState<T>(() => { |
||||
|
try { |
||||
|
const item = window.localStorage.getItem(key); |
||||
|
return item ? (JSON.parse(item) as T) : initialValue; |
||||
|
} catch (error) { |
||||
|
console.error("Error reading localStorage key “" + key + "”:", error); |
||||
|
return initialValue; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const setValue = (value: T | ((val: T) => T)) => { |
||||
|
try { |
||||
|
const valueToStore = value instanceof Function ? value(storedValue) : value; |
||||
|
setStoredValue(valueToStore); |
||||
|
window.localStorage.setItem(key, JSON.stringify(valueToStore)); |
||||
|
} catch (error) { |
||||
|
console.error("Error setting localStorage key “" + key + "”:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const handleStorageChange = () => { |
||||
|
try { |
||||
|
const item = window.localStorage.getItem(key); |
||||
|
setStoredValue(item ? (JSON.parse(item) as T) : initialValue); |
||||
|
} catch (error) { |
||||
|
console.error("Error reading localStorage key “" + key + "”:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
window.addEventListener("storage", handleStorageChange); |
||||
|
return () => window.removeEventListener("storage", handleStorageChange); |
||||
|
}, [key, initialValue]); |
||||
|
|
||||
|
return [storedValue, setValue]; |
||||
|
} |
||||
|
|
||||
|
export default useLocalStorage; |
@ -1,22 +1,23 @@ |
|||||
import Header from "@/components/layout/header"; |
import Header from "@/components/layout/header"; |
||||
import SideBar from "@/components/layout/sidebar"; |
import SideBar from "@/components/layout/sidebar"; |
||||
|
import FooterSticky from "@/components/ui/footer-sticky"; |
||||
import "@/styles/globals.css"; |
import "@/styles/globals.css"; |
||||
import type { AppProps } from "next/app"; |
import type { AppProps } from "next/app"; |
||||
|
|
||||
export default function App({ Component, pageProps }: AppProps) { |
export default function App({ Component, pageProps }: AppProps) { |
||||
console.log(pageProps); |
console.log(pageProps); |
||||
|
|
||||
return ( |
return ( |
||||
<> |
<> |
||||
<Header /> |
<Header /> |
||||
<div className="max-w-[1440px] m-auto"> |
|
||||
<div className="p-11 flex gap-11"> |
|
||||
|
<div className=" m-auto bg-[#EAEAEA] lg:p-6"> |
||||
|
<div className="max-w-[1440px] flex flex-col lg:flex-row gap-6 relative"> |
||||
<SideBar /> |
<SideBar /> |
||||
<main className="flex-grow"> |
|
||||
|
<main className={`w-full`}> |
||||
<Component {...pageProps} /> |
<Component {...pageProps} /> |
||||
</main> |
</main> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
<FooterSticky/> |
||||
</> |
</> |
||||
); |
); |
||||
} |
} |
@ -0,0 +1,38 @@ |
|||||
|
import Image from "next/image"; |
||||
|
import img from "../../public/assets/images/jamkaran.png"; |
||||
|
import { useParams, useRouter } from "next/router"; |
||||
|
|
||||
|
const About = () => { |
||||
|
return ( |
||||
|
<div className="w-full flex items-center justify-center"> |
||||
|
<div className="w-[660px]"> |
||||
|
<p> |
||||
|
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam |
||||
|
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, |
||||
|
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea |
||||
|
rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem |
||||
|
ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur |
||||
|
sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et |
||||
|
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam |
||||
|
et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea |
||||
|
takimata sanctus est Lorem ipsum dolor sit amet. gubergren, no sea |
||||
|
takimata sanctus est Lorem ipsum dolor sit amet Lorem ipsum dolor sit |
||||
|
amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor |
||||
|
invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. |
||||
|
At vero eos et accusam et justo duo dolores et ea rebum. Stet clita |
||||
|
kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit |
||||
|
amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed |
||||
|
diam nonumy. |
||||
|
</p> |
||||
|
<Image src={img} alt="photo" /> |
||||
|
<p> |
||||
|
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam |
||||
|
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, |
||||
|
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea |
||||
|
rebum. Stet clita kasd gubergren, no sea takimata{" "} |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
export default About; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue