├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── api │ └── geolocation │ │ └── routes.ts ├── brand.ico ├── globals.css ├── layout.tsx ├── niat │ ├── Niat.tsx │ └── page.tsx ├── page.tsx ├── resep │ ├── Resep.tsx │ └── page.tsx └── tadarus │ ├── Tadarus.tsx │ ├── page.tsx │ └── surah │ └── [id] │ ├── Surah.tsx │ └── page.tsx ├── components ├── audioButton │ └── page.tsx ├── audioButtonAyahs │ └── page.tsx ├── geocoding │ └── page.tsx ├── hero │ └── page.tsx ├── notification │ └── page.tsx ├── particle │ └── page.tsx ├── quran │ └── page.tsx ├── ramadhanCountdown │ └── page.tsx ├── star │ └── page.tsx ├── surahDetail │ └── page.tsx └── weather │ └── page.tsx ├── fonts ├── AmazingRamadhan.otf ├── Grako-Demo-Regular.otf ├── Kahlil.otf ├── OpenSans-Bold.otf ├── QiyamuRamadhan.otf ├── RamadhanAmazing-jEnDv.ttf ├── SanshiroDemo-Black.otf ├── coolvetica rg.otf └── monasgrotesk-bold.otf ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── brand.ico ├── img │ ├── bunderan.png │ ├── bxs-home.svg │ ├── gapura.png │ ├── gapura2.png │ ├── home.png │ └── lantern.png ├── next.svg └── vercel.svg ├── redux ├── rootReducer.ts ├── store.ts ├── surahActions.ts └── surahReducer.ts ├── tailwind.config.ts ├── tsconfig.json └── utils └── config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /app/api/geolocation/routes.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | export async function GET(request: Request) { 4 | try { 5 | const { searchParams } = new URL(request.url); 6 | const latitude = searchParams.get('latitude'); 7 | const longitude = searchParams.get('longitude'); 8 | 9 | const API_KEY = process.env.OPENCAGE_API_KEY; 10 | 11 | const response = await fetch( 12 | `https://api.opencagedata.com/geocode/v1/json?q=${latitude}+${longitude}&key=${API_KEY}&language=id` 13 | ); 14 | 15 | const data = await response.json(); 16 | return NextResponse.json(data); 17 | 18 | 19 | } catch (error) { 20 | return NextResponse.json( 21 | { error: 'Failed to fetch location' }, 22 | { status: 500 } 23 | ); 24 | } 25 | } -------------------------------------------------------------------------------- /app/brand.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klawcodes/awas-imsak/81c4396c1498c86331b66efb93dbfa3da4f4cd16/app/brand.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"); 6 | 7 | @font-face { 8 | font-family: 'open'; 9 | src: url('../fonts/OpenSans-Bold.otf'); 10 | } 11 | 12 | @font-face { 13 | font-family: 'sansi'; 14 | src: url('../fonts/SanshiroDemo-Black.otf'); 15 | } 16 | 17 | @font-face { 18 | font-family: 'aramadhan'; 19 | src: url('../fonts/AmazingRamadhan.otf'); 20 | } 21 | 22 | @font-face { 23 | font-family: 'grako'; 24 | src: url('../fonts/Grako-Demo-Regular.otf'); 25 | } 26 | 27 | @font-face { 28 | font-family: 'monas'; 29 | src: url('../fonts/monasgrotesk-bold.otf'); 30 | } 31 | 32 | @font-face { 33 | font-family: 'kahlil'; 34 | src: url('../fonts/Kahlil.otf'); 35 | } 36 | 37 | @font-face { 38 | font-family: 'qramadhan'; 39 | src: url('../fonts/QiyamuRamadhan.otf'); 40 | } 41 | 42 | @font-face { 43 | font-family: 'aaramadhan'; 44 | src: url('../fonts/RamadhanAmazing-jEnDv.ttf'); 45 | } 46 | 47 | @font-face { 48 | font-family: 'coolvetica'; 49 | src: url('../fonts/coolvetica\ rg.otf'); 50 | } 51 | 52 | /* styles/starryBackground.css */ 53 | .bodi { 54 | font-family: "Poppins", sans-serif; 55 | font-weight: 300; 56 | font-style: normal; 57 | background: linear-gradient(30deg, #0d0a0b, #0d2818, #0d0a0b); 58 | color: white; 59 | background-size: 200% auto; 60 | animation: gradientAnimation 50s ease-in-out infinite; 61 | overflow-x: hidden; 62 | min-height: 100vh; 63 | margin: 0; 64 | position: relative; 65 | } 66 | 67 | @keyframes gradientAnimation { 68 | 0% { background-position: 0% 50%; } 69 | 50% { background-position: 100% 50%; } 70 | 100% { background-position: 0% 50%; } 71 | } 72 | 73 | .stars { 74 | position: fixed; 75 | top: 0; 76 | left: 0; 77 | width: 100%; 78 | height: 100%; 79 | pointer-events: none; 80 | z-index: 0; 81 | } 82 | 83 | .star { 84 | position: absolute; 85 | width: 2px; 86 | height: 2px; 87 | background: white; 88 | border-radius: 50%; 89 | opacity: 0; 90 | animation: twinkle var(--duration) ease-in-out infinite; 91 | animation-delay: var(--delay); 92 | } 93 | 94 | @keyframes twinkle { 95 | 0%, 100% { opacity: 0; transform: scale(0.3); } 96 | 50% { opacity: 0.8; transform: scale(1); } 97 | } 98 | 99 | .bodi-niat { 100 | min-height: 117vh; 101 | } 102 | 103 | .bodi-resep { 104 | min-height: 100vh; 105 | } 106 | 107 | .bodi-tadarus { 108 | min-height: 100vh; 109 | } 110 | 111 | @media only screen and (max-width: 640px) { 112 | .bodi { 113 | min-height: 100vh; 114 | } 115 | 116 | .bodi-hero { 117 | min-height: 160vh; 118 | } 119 | 120 | /*.parent { 121 | display: grid; 122 | grid-template-columns: 1fr; 123 | grid-template-rows: repeat(12, 1fr); 124 | grid-column-gap: 0px; 125 | grid-row-gap: 16px; 126 | }*/ 127 | 128 | 129 | } 130 | 131 | @media only screen and (max-width: 414px) { 132 | .bodi-hero { 133 | min-height: 215vh 134 | } 135 | 136 | .bodi-niat { 137 | min-height: 80vh; 138 | } 139 | } 140 | 141 | 142 | 143 | @keyframes gradientAnimation { 144 | 0% { 145 | background-position: 0% 50%; /* Mulai dari posisi kiri tengah */ 146 | } 147 | 50% { 148 | background-position: 100% 50%; /* Posisi di tengah jalan saat animasi */ 149 | } 150 | 100% { 151 | background-position: 0% 50%; /* Kembali ke posisi awal */ 152 | } 153 | } 154 | 155 | 156 | .poppins-thin { 157 | font-family: "Poppins", sans-serif; 158 | font-weight: 100; 159 | font-style: normal; 160 | } 161 | 162 | .poppins-extralight { 163 | font-family: "Poppins", sans-serif; 164 | font-weight: 200; 165 | font-style: normal; 166 | } 167 | 168 | .poppins-light { 169 | font-family: "Poppins", sans-serif; 170 | font-weight: 300; 171 | font-style: normal; 172 | } 173 | 174 | .poppins-regular { 175 | font-family: "Poppins", sans-serif; 176 | font-weight: 400; 177 | font-style: normal; 178 | } 179 | 180 | .poppins-medium { 181 | font-family: "Poppins", sans-serif; 182 | font-weight: 500; 183 | font-style: normal; 184 | } 185 | 186 | .poppins-semibold { 187 | font-family: "Poppins", sans-serif; 188 | font-weight: 600; 189 | font-style: normal; 190 | } 191 | 192 | .poppins-bold { 193 | font-family: "Poppins", sans-serif; 194 | font-weight: 700; 195 | font-style: normal; 196 | } 197 | 198 | .poppins-extrabold { 199 | font-family: "Poppins", sans-serif; 200 | font-weight: 800; 201 | font-style: normal; 202 | } 203 | 204 | .poppins-black { 205 | font-family: "Poppins", sans-serif; 206 | font-weight: 900; 207 | font-style: normal; 208 | } 209 | 210 | .poppins-thin-italic { 211 | font-family: "Poppins", sans-serif; 212 | font-weight: 100; 213 | font-style: italic; 214 | } 215 | 216 | .poppins-extralight-italic { 217 | font-family: "Poppins", sans-serif; 218 | font-weight: 200; 219 | font-style: italic; 220 | } 221 | 222 | .poppins-light-italic { 223 | font-family: "Poppins", sans-serif; 224 | font-weight: 300; 225 | font-style: italic; 226 | } 227 | 228 | .poppins-regular-italic { 229 | font-family: "Poppins", sans-serif; 230 | font-weight: 400; 231 | font-style: italic; 232 | } 233 | 234 | .poppins-medium-italic { 235 | font-family: "Poppins", sans-serif; 236 | font-weight: 500; 237 | font-style: italic; 238 | } 239 | 240 | .poppins-semibold-italic { 241 | font-family: "Poppins", sans-serif; 242 | font-weight: 600; 243 | font-style: italic; 244 | } 245 | 246 | .poppins-bold-italic { 247 | font-family: "Poppins", sans-serif; 248 | font-weight: 700; 249 | font-style: italic; 250 | } 251 | 252 | .poppins-extrabold-italic { 253 | font-family: "Poppins", sans-serif; 254 | font-weight: 800; 255 | font-style: italic; 256 | } 257 | 258 | .poppins-black-italic { 259 | font-family: "Poppins", sans-serif; 260 | font-weight: 900; 261 | font-style: italic; 262 | } 263 | 264 | .opensans { 265 | font-family: 'open'; 266 | } 267 | 268 | .sanshiro { 269 | font-family: 'sansi'; 270 | } 271 | 272 | .grako { 273 | font-family: 'grako'; 274 | } 275 | 276 | .monas { 277 | font-family: 'monas'; 278 | } 279 | 280 | .kahlil { 281 | font-family: 'kahlil'; 282 | } 283 | 284 | .aramadhan { 285 | font-family: 'aaramadhan'; 286 | } 287 | 288 | .qramadhan { 289 | font-family: 'qramadhan'; 290 | } 291 | 292 | .coolvetica { 293 | font-family: 'coolvetica'; 294 | } 295 | 296 | .active-box { 297 | background-image: linear-gradient(to bottom, #0d2818, #0d0a0b); 298 | background-size: 100% 200%; /* Menentukan ukuran latar belakang */ 299 | animation: rareEffect 1s infinite ease-in-out alternate; /* Durasi, jenis, dan iterasi animasi */ 300 | } 301 | 302 | .active-shadow { 303 | box-shadow: 0px 0px 25px -1px rgba(0,0,0,0.42); 304 | } 305 | 306 | .pagination { 307 | display: flex; 308 | justify-content: center; 309 | align-items: center; 310 | margin-top: 2rem; 311 | } 312 | .page-link { 313 | padding: 0.5rem 1rem; 314 | margin: 0 0.25rem; 315 | border: 1px solid #3e664e; /* Warna border yang sama dengan div di atasnya */ 316 | border-radius: 4px; 317 | color: #3e664e; 318 | cursor: pointer; 319 | } 320 | .page-link:hover { 321 | background-color: #3e664e; 322 | color: white; 323 | } 324 | .active { 325 | background-color: #3e664e; 326 | color: whitesmoke; 327 | 328 | } 329 | .disabled { 330 | opacity: 0.5; 331 | cursor: not-allowed; 332 | } 333 | 334 | input[type=range]::-webkit-slider-runnable-track { 335 | background-color: #3e664e; /* Ubah warna sesuai kebutuhan Anda */ 336 | } 337 | 338 | 339 | @keyframes rareEffect { 340 | 0% { 341 | background-position: 0% 0%; /* Posisi awal gradient */ 342 | } 343 | 100% { 344 | background-position: 0% 100%; /* Posisi akhir gradient */ 345 | } 346 | } 347 | 348 | @keyframes fadeIn { 349 | from { 350 | opacity: 0; 351 | transform: translateY(-10px); 352 | } 353 | to { 354 | opacity: 1; 355 | transform: translateY(0); 356 | } 357 | } 358 | 359 | .animate-fade-in { 360 | animation: fadeIn 0.5s ease-out forwards; 361 | } 362 | 363 | 364 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import StarryBackground from "@/components/star/page"; 5 | 6 | 7 | export const metadata: Metadata = { 8 | title: "Awas Imsak! - Jadwal Sholat & Imsak", 9 | icons: { 10 | icon: '/brand.ico' 11 | }, 12 | description: "Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses", 13 | applicationName: 'Awas Imsak!', 14 | referrer: 'origin-when-cross-origin', 15 | keywords: ['puasa', 'imsak', 'sholat', 'ramadhan', 'jadwal imsak', 'jadwal sholat', 'quran', 'baca quran', 'puasa ramadhan', 'bulan ramadhan'], 16 | authors: [{ name: 'Klaw' }, { name: 'Muhammad Dimas', url: 'https://klaw.my.id' }], 17 | creator: 'Klaw', 18 | publisher: 'RIOT REVENGER', 19 | formatDetection: { 20 | email: false, 21 | address: false, 22 | telephone: false, 23 | }, 24 | openGraph: { 25 | title: 'Awas Imsak! - Jadwal Sholat & Imsak', 26 | description: 'Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses', 27 | url: 'https://imsak.my.id', 28 | siteName: 'Awas Imsak!', 29 | type: 'website', 30 | }, 31 | }; 32 | 33 | export default function RootLayout({ 34 | children, 35 | }: Readonly<{ 36 | children: React.ReactNode; 37 | }>) { 38 | return ( 39 | 40 | 41 | 42 | 43 | {children} 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /app/niat/Niat.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | const Niat = () => { 4 | return ( 5 |
6 |
7 |
8 | 9 |
10 |

11 | ⬅ Back to Home 12 |

13 |
14 | 15 |

29 | Awas Lupa Niat 30 |

31 |

Awas kawan jangan sampai lupa!

32 |
33 |
34 |
35 |

36 | Niat Puasa Ramadhan untuk Sehari: 37 |

38 |

39 | نَوَيْتُ صَوْمَ غَدٍ عَنْ أَدَاءِ فَرْضِ شَهْرِ رَمَضَانَ هَذِهِ 40 | السَّنَةِ لِلّٰهِ تَعَالَى 41 |

42 |

43 | Nawaitu shauma ghadin 'an ada'i fardhi syahri Ramadhana 44 | hadzihis sanati lillahi ta'ala. 45 |

46 |

47 | Artinya: “Aku niat berpuasa esok hari untuk menunaikan kewajiban 48 | puasa bulan Ramadhan tahun ini, karena Allah Ta'ala.” 49 |

50 |
51 |
52 |

53 | Niat Puasa Ramadhan untuk Sebulan Penuh: 54 |

55 |

56 | نَوَيْتُ صَوْمَ جَمِيْعِ شَهْرِ رَمَضَانِ هٰذِهِ السَّنَةِ فَرْضًا 57 | لِلّٰهِ تَعَالَى 58 |

59 |

60 | Nawaitu shauma jami'i syahri Ramadhani hadzihis sanati 61 | fardhan lillahi ta'ala. 62 |

63 |

64 | Artinya: “Aku niat berpuasa di sepanjang bulan Ramadhan tahun ini 65 | dengan mengikuti pendapat Imam Malik, wajib karena Allah 66 | Ta'ala.” 67 |

68 |
69 |
70 |
71 | 72 |
73 | Awas Lupa Tadarus 74 |
75 | 76 | 77 |
78 | Awas Lupa Buka 79 |
80 | 81 |
82 |
83 |
84 | ); 85 | } 86 | 87 | export default Niat -------------------------------------------------------------------------------- /app/niat/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { Metadata } from "next"; 3 | import Niat from './Niat' 4 | 5 | export const metadata: Metadata = { 6 | title: "Awas Imsak! - Niat Puasa Bulan Ramadhan", 7 | description: "Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses", 8 | }; 9 | 10 | const NiatPage = () => { 11 | return ( 12 |
13 | ) 14 | } 15 | 16 | export default NiatPage -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState, useEffect } from 'react'; 4 | import Hero from '../components/hero/page' 5 | 6 | export default function Home() { 7 | 8 | const [countdown, setCountdown] = useState(''); 9 | const [countdownFinished, setCountdownFinished] = useState(false); 10 | 11 | // state baru untuk control tampil/hilang text 12 | const [showText, setShowText] = useState(true); 13 | 14 | useEffect(() => { 15 | 16 | const targetDate = new Date('2024-03-11T18:30:00').getTime(); 17 | 18 | const timer = setInterval(() => { 19 | 20 | const now = new Date().getTime(); 21 | 22 | const distance = targetDate - now; 23 | 24 | if(distance <= 0) { 25 | 26 | clearInterval(timer); 27 | 28 | setCountdownFinished(true); 29 | setShowText(true); 30 | 31 | setTimeout(() => { 32 | setCountdown(''); 33 | setShowText(false); 34 | }, 3000); 35 | 36 | } else { 37 | 38 | // hitung countdown 39 | const days = Math.floor(distance / (1000 * 60 * 60 * 24)); 40 | const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); 41 | const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); 42 | const seconds = Math.floor((distance % (1000 * 60)) / 1000); 43 | 44 | setCountdown(`${days} hari ${hours} jam ${minutes} menit ${seconds} detik`); 45 | } 46 | 47 | }, 1000); 48 | 49 | return () => clearInterval(timer); 50 | 51 | }, []); 52 | 53 | // effect untuk handle timeout 54 | useEffect(() => { 55 | if(countdownFinished) { 56 | setTimeout(() => { 57 | setShowText(false); 58 | }, 3000); 59 | } 60 | }, [countdownFinished]); 61 | 62 | return ( 63 | <> 64 | {/* Tampilkan penomoran halaman {!countdownFinished && ( 65 | <> 66 |
67 |

84 | {countdown} 85 |

86 |

Menuju Puasa Ramadhan 1445 H / 2024 M

87 |

"What makes your sorry different from all your other sorrys before?"

88 |
89 |

102 | Awas Imsak! 103 |

104 |

105 | © 2024 Klaw, under RIOT REVENGER exclusive agreements. 106 |

107 |
108 |
109 | 110 | )} 111 | 112 | {countdownFinished && showText && ( 113 | <> 114 |
115 |

131 | Selamat menunaikan ibadah Puasa! 132 |

133 |
134 | 135 | )} 136 | 137 | {countdownFinished && !showText && }*/} 138 | 139 | 140 | ); 141 | } 142 | -------------------------------------------------------------------------------- /app/resep/Resep.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React, { useState, useEffect } from 'react'; 4 | import Image from 'next/image' 5 | import Link from 'next/link' 6 | import ReactPaginate from 'react-paginate'; 7 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 8 | import { faClock, faBookOpen, faSearch } from '@fortawesome/free-solid-svg-icons'; 9 | 10 | interface Resep { 11 | title: string; 12 | time: string; 13 | difficulty: string; 14 | 'image-src': string; 15 | 'link-href': string; 16 | } 17 | 18 | const Resep: React.FC = () => { 19 | const [resepList, setResepList] = useState([]); 20 | const [searchKeyword, setSearchKeyword] = useState(''); 21 | const [currentPage, setCurrentPage] = useState(0); 22 | const resepPerPage = 6; 23 | const [pageRangeDisplayed, setPageRangeDisplayed] = useState(5); 24 | const [marginPagesDisplayed, setMarginPagesDisplayed] = useState(2); 25 | 26 | useEffect(() => { 27 | fetch('https://mahi-unofficial.netlify.app/.netlify/functions/server/makanMalam', { 28 | method: 'GET', 29 | headers: { 30 | 'Content-Type': 'application/json', 31 | }, 32 | }) 33 | .then(response => { 34 | if (!response.ok) { 35 | throw new Error(`HTTP error! status: ${response.status}`); 36 | } 37 | return response.json(); 38 | }) 39 | .then((data: Resep[]) => setResepList(data)) 40 | .catch(error => console.error('Error fetching data:', error)); 41 | }, []); 42 | 43 | // Fungsi untuk menangani perubahan halaman 44 | const handlePageChange = ({ selected }: { selected: number }) => { 45 | setCurrentPage(selected); 46 | }; 47 | 48 | // Fungsi untuk melakukan filter berdasarkan keyword pencarian 49 | const filteredResep = resepList.filter(resep => 50 | resep.title.toLowerCase().includes(searchKeyword.toLowerCase()) 51 | ); 52 | 53 | // Menampilkan resep sesuai halaman yang dipilih 54 | const indexOfLastResep = (currentPage + 1) * resepPerPage; 55 | const indexOfFirstResep = indexOfLastResep - resepPerPage; 56 | const currentResep = filteredResep.slice(indexOfFirstResep, indexOfLastResep); 57 | 58 | // Mengubah nilai pageRangeDisplayed berdasarkan lebar layar 59 | useEffect(() => { 60 | const handleResize = () => { 61 | setPageRangeDisplayed(window.innerWidth <= 640 ? 2 : 5); 62 | setMarginPagesDisplayed(window.innerWidth <= 640 ? 0 : 2); 63 | }; 64 | 65 | handleResize(); 66 | 67 | window.addEventListener('resize', handleResize); 68 | 69 | return () => { 70 | window.removeEventListener('resize', handleResize); 71 | }; 72 | }, []); 73 | 74 | return ( 75 | <> 76 |
77 | 81 |
82 |

83 | ⬅ Back to Home 84 |

85 |
86 | 87 |

101 | Awas Lupa Masak 102 |

103 |

104 | Masa udah puasa malah ga buka gara-gara ga masak. 105 |

106 |
107 |
108 |
109 | setSearchKeyword(e.target.value)} 114 | className="bg-[#0d1811] border border-[#3e664e] text-white px-10 py-2 rounded-lg w-[20%] mb-4 mt-5 max-[640px]:w-[60%]" 115 | /> 116 |
117 | 118 |
119 |
120 |
121 | 122 | {/* Menampilkan pesan jika hasil pencarian tidak ditemukan */} 123 | {filteredResep.length === 0 && ( 124 |
125 |

Yah... masakannya ga ketemu, coba cari yang lain dehh

126 |
127 | )} 128 |
129 | {currentResep.map((resep, index) => ( 130 |
134 | 135 | {resep.title} 143 | 144 |

{resep.title}

145 |
146 |
147 | {" "} 148 | {resep.time ? resep.time : " -"} 149 |
150 |
151 | {resep.difficulty ? resep.difficulty : " -"} 152 |
153 | 157 | 158 | 159 |
160 |
161 | ))} 162 |
163 |
164 | {/* Tampilkan penomoran halaman */} 165 | ←} 173 | nextLabel={} 174 | breakLabel={"..."} 175 | pageLinkClassName={"page-link"} 176 | previousLinkClassName={"page-link"} 177 | nextLinkClassName={"page-link"} 178 | disabledClassName={"disabled"} 179 | /> 180 |
181 | 182 | ); 183 | }; 184 | 185 | export default Resep; 186 | -------------------------------------------------------------------------------- /app/resep/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { Metadata } from "next"; 3 | import Resep from './Resep' 4 | 5 | export const metadata: Metadata = { 6 | title: "Awas Imsak! - Resep masak untuk keluarga.", 7 | description: "Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses", 8 | }; 9 | 10 | const ResepPage = () => { 11 | return ( 12 |
13 | ) 14 | } 15 | 16 | export default ResepPage -------------------------------------------------------------------------------- /app/tadarus/Tadarus.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Quran from "../../components/quran/page"; 4 | import { Provider } from 'react-redux'; 5 | import store from '../../redux/store'; 6 | import Link from 'next/link' 7 | 8 | const Tadarus = () => { 9 | return ( 10 | <> 11 |
12 |
13 | 14 |
15 |

16 | ⬅ Back to Home

17 |
18 | 19 |

37 | Awas Lupa Tadarus 38 |

39 |

40 | Jangan lupa tadarus di sini! Baca quran online, buat gadget anda lebih berfaedah! 41 |

42 |
43 |
44 | 45 | 46 | 47 |
48 |
49 | 50 | ); 51 | }; 52 | 53 | export default Tadarus; -------------------------------------------------------------------------------- /app/tadarus/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { Metadata } from "next"; 3 | import Tadarus from './Tadarus' 4 | 5 | export const metadata: Metadata = { 6 | title: "Awas Imsak! - Baca Quran Online", 7 | description: "Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses", 8 | }; 9 | 10 | const TadarusPage = () => { 11 | return ( 12 |
13 | ) 14 | } 15 | 16 | export default TadarusPage -------------------------------------------------------------------------------- /app/tadarus/surah/[id]/Surah.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | import { usePathname, useRouter, useSearchParams } from "next/navigation"; 5 | import SurahDetail from "../../../../components/surahDetail/page"; 6 | import AudioButtonAyahs from "../../../../components/audioButtonAyahs/page" 7 | import AudioButtonWithSlider from "../../../../components/audioButton/page" 8 | import Link from 'next/link' 9 | import Image from 'next/image' 10 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 11 | import { faArrowLeft, faArrowRight, faArrowRotateLeft, faHome } from '@fortawesome/free-solid-svg-icons'; 12 | 13 | interface Ayah { 14 | number: { 15 | inquran: number; 16 | insurah: number; 17 | }; 18 | juz: number; 19 | manzil: number; 20 | page: number; 21 | ruku: number; 22 | hizbQuarter: number; 23 | sajda: { 24 | recommended: boolean; 25 | obligatory: boolean; 26 | }; 27 | text: { 28 | ar: string; 29 | read: string; 30 | }; 31 | translation: { 32 | en: string; 33 | id: string; 34 | }; 35 | tafsir: { 36 | id: string; 37 | en: string | null; 38 | }; 39 | audio: { 40 | url: string; 41 | }; 42 | } 43 | 44 | // Definisikan struktur data surah 45 | interface SurahData { 46 | number: number; 47 | ayahCount: number; 48 | sequence: number; 49 | asma: { 50 | ar: { 51 | short: string; 52 | long: string; 53 | }; 54 | en: { 55 | short: string; 56 | long: string; 57 | }; 58 | id: { 59 | short: string; 60 | long: string; 61 | }; 62 | translation: { 63 | en: string; 64 | id: string; 65 | }; 66 | }; 67 | preBismillah: any; 68 | type: { 69 | ar: string; 70 | id: string; 71 | en: string; 72 | }; 73 | tafsir: { 74 | id: string; 75 | en: string | null; 76 | }; 77 | recitation: { 78 | full: string; 79 | }; 80 | ayahs: Ayah[]; 81 | } 82 | 83 | const Icons = { 84 | width: '30px', 85 | height: 'auto', 86 | '@media (min-width: 360px)': { 87 | width: '5px' // Ubah lebar gambar saat lebar layar <= 640px 88 | }, 89 | fill: 'white' // Mengatur warna ikon menjadi putih 90 | }; 91 | 92 | const SurahDetails: React.FC = () => { 93 | const searchParams = useSearchParams(); 94 | const pathname = usePathname(); 95 | const surahId = searchParams.get("surahId"); 96 | const [surahData, setSurahData] = useState(null); 97 | const [showTafsir, setShowTafsir] = useState(false); 98 | const [previousSurahName, setPreviousSurahName] = useState(''); 99 | const [nextSurahName, setNextSurahName] = useState(''); 100 | const [isOn, setIsOn] = useState(false); 101 | const [activeAyahs, setActiveAyahs] = useState([]); 102 | 103 | 104 | 105 | { 106 | /* useEffect(() => { 107 | // Cek apakah sedang berada di lingkungan klien 108 | if (typeof window !== "undefined" && surahId) { 109 | const fetchSurahData = async () => { 110 | try { 111 | const response = await fetch( 112 | `https://quran-endpoint.vercel.app/quran/${surahId}` 113 | ); 114 | if (!response.ok) { 115 | throw new Error("Failed to fetch data"); 116 | } 117 | const data = await response.json(); 118 | setSurahData(data); 119 | } catch (error) { 120 | console.error("Error fetching surah data:", error); 121 | } 122 | }; 123 | 124 | fetchSurahData(); 125 | } 126 | // Mendapatkan URL saat ini 127 | //const currentURL = window.location.href; 128 | 129 | // Memisahkan URL berdasarkan tanda '/' (slash) 130 | //const urlParts = currentURL.split("/"); 131 | 132 | // Mengambil parameter terakhir 133 | //const lastParam = urlParts[urlParts.length - 1]; 134 | 135 | //console.log(lastParam); // Ini akan mencetak keluar "1?" 136 | 137 | //const url = `${pathname}?${searchParams}` 138 | //console.log(url) 139 | }, [pathname, searchParams, surahId]);*/ 140 | } 141 | 142 | const getPageLocalStorageKey = () => { 143 | if (typeof window !== "undefined") { 144 | return `activeAyahs_${window.location.pathname}`; 145 | } 146 | return ""; 147 | }; 148 | 149 | useEffect(() => { 150 | const storedData = localStorage.getItem(getPageLocalStorageKey()); 151 | if (storedData) { 152 | setActiveAyahs(JSON.parse(storedData)); 153 | } 154 | }, []); 155 | 156 | 157 | useEffect(() => { 158 | if (typeof window !== "undefined") { 159 | const currentURL = window.location.href; 160 | 161 | const urlParts = currentURL.split("/"); 162 | 163 | const surahId = urlParts[urlParts.length - 1]; 164 | 165 | const fetchSurahData = async () => { 166 | try { 167 | const response = await fetch( 168 | `https://quran-endpoint.vercel.app/quran/${surahId}` 169 | ); 170 | if (!response.ok) { 171 | throw new Error("Failed to fetch data"); 172 | } 173 | const data = await response.json(); 174 | setSurahData(data); 175 | } catch (error) { 176 | console.error("Error fetching surah data:", error); 177 | } 178 | }; 179 | 180 | if (surahId) { 181 | fetchSurahData(); 182 | } 183 | } 184 | }, []); 185 | 186 | if (!surahData) { 187 | return ( 188 | <> 189 |
198 |
199 |
200 | 201 | ); 202 | } 203 | 204 | const { ayahs } = surahData.data; 205 | const allAyahTextsRead = ayahs.map((ayah: { text: any }) => ayah.text.read); 206 | const allAyahTransId = ayahs.map( 207 | (ayah: { translation: any }) => ayah.translation.id 208 | ); 209 | const allAyahNumbers = ayahs.map( 210 | (ayah: { number: any }) => ayah.number.insurah 211 | ); 212 | const allAyahTextsAr = ayahs.map((ayah: { text: any }) => ayah.text.ar); 213 | const allAyahAudio = ayahs.map((ayah: { audio: any }) => ayah.audio.url); 214 | 215 | const navigateToSurah = (surahNumber: number) => { 216 | // Pastikan nomor surah berada dalam rentang yang valid (1-114) 217 | if (surahNumber >= 1 && surahNumber <= 114) { 218 | // Lakukan navigasi ke surah tertentu 219 | window.location.href = `/tadarus/surah/${surahNumber}`; 220 | } 221 | }; 222 | 223 | 224 | 225 | 226 | 227 | const handleClick = (index: number) => { 228 | let newActiveAyahs: number[] = []; 229 | if (activeAyahs.includes(index)) { 230 | newActiveAyahs = activeAyahs.filter((ayahIndex) => ayahIndex !== index); 231 | } else { 232 | newActiveAyahs = [index]; 233 | } 234 | localStorage.setItem(getPageLocalStorageKey(), JSON.stringify(newActiveAyahs)); 235 | setActiveAyahs(newActiveAyahs); 236 | }; 237 | 238 | 239 | 240 | return ( 241 |
242 |
243 |
244 | 245 |
246 | Al-Fatihah 247 |
248 | 249 | {surahData && surahData.data.number !== 1 && ( 250 | <> 251 | 258 | 259 | )} 260 | 261 | 262 | 263 | 264 | 265 | 266 | {surahData && surahData.data.number !== 114 && ( 267 | 273 | )} 274 | 275 |
276 | An-Nas 277 |
278 | 279 |
280 |

{surahData.data.asma.ar.short}

281 |

295 | {surahData.data.asma.id.short} 296 |

297 | 298 |
299 |

{surahData.data.ayahCount} Ayat

300 |

{surahData.data.type.id}

301 |
302 |
303 | 309 | 310 | {showTafsir && ( 311 |
312 |
313 |

Tafsir:

314 |

{surahData.data.tafsir.id}

315 |
316 |
317 | )} 318 |
319 | 323 |
324 |
325 |
    326 | {surahData.data.preBismillah && ( 327 |
    328 |

    {surahData.data.preBismillah.text.ar}

    329 |

    330 | {surahData.data.preBismillah.text.read} 331 |

    332 |

    {surahData.data.preBismillah.translation.id}

    333 |
    334 | )} 335 | {allAyahTextsAr.map((text: string, index: number) => ( 336 |
    340 |
    341 | 353 | 354 |
    {text}
    355 |
    356 |
    {allAyahTextsRead[index]}
    357 |
    {allAyahTransId[index]}
    358 | 359 |
    360 | ))} 361 |
362 |
363 |
364 | 365 |
366 | Al-Fatihah 367 |
368 | 369 | {surahData && surahData.data.number !== 1 && ( 370 | <> 371 | 378 | 379 | )} 380 | 381 | 382 | 383 | 384 | 385 | 386 | {surahData && surahData.data.number !== 114 && ( 387 | 393 | )} 394 | 395 |
396 | An-Nas 397 |
398 | 399 |
400 |
401 | ); 402 | }; 403 | 404 | export default SurahDetails; 405 | -------------------------------------------------------------------------------- /app/tadarus/surah/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { Metadata } from "next"; 3 | import Surah from './Surah' 4 | 5 | export const metadata: Metadata = { 6 | title: "Tadarus - Baca Quran Online", 7 | description: "Awas Imsak! adalah portal yang menghadirkan jadwal sholat dan imsak dengan akurat dan mudah diakses", 8 | }; 9 | 10 | const SurahPage = () => { 11 | return ( 12 |
13 | ) 14 | } 15 | 16 | export default SurahPage -------------------------------------------------------------------------------- /components/audioButton/page.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from 'react'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import { faPlay, faPause } from '@fortawesome/free-solid-svg-icons'; 4 | 5 | interface AudioButtonProps { 6 | audioSource: string; 7 | description: string; 8 | } 9 | 10 | const AudioButtonWithSlider: React.FC = ({ audioSource, description }) => { 11 | const audioRef = useRef(null); 12 | const [isPlaying, setIsPlaying] = useState(false); 13 | const [currentTime, setCurrentTime] = useState(0); 14 | const [duration, setDuration] = useState(0); 15 | 16 | const togglePlay = () => { 17 | if (audioRef.current) { 18 | if (isPlaying) { 19 | audioRef.current.pause(); 20 | } else { 21 | audioRef.current.play(); 22 | } 23 | setIsPlaying(!isPlaying); 24 | } 25 | }; 26 | 27 | const handleTimeUpdate = () => { 28 | if (audioRef.current) { 29 | setCurrentTime(audioRef.current.currentTime); 30 | setDuration(audioRef.current.duration); 31 | } 32 | }; 33 | 34 | const handleSliderChange = (e: React.ChangeEvent) => { 35 | if (audioRef.current) { 36 | const newTime = parseInt(e.target.value); 37 | audioRef.current.currentTime = newTime; 38 | setCurrentTime(newTime); 39 | } 40 | }; 41 | 42 | const handleAudioEnded = () => { 43 | if (audioRef.current) { 44 | setIsPlaying(false); 45 | setCurrentTime(0); 46 | audioRef.current.currentTime = 0; 47 | } 48 | }; 49 | 50 | 51 | const formatTime = (time: number) => { 52 | const minutes = Math.floor(time / 60); 53 | const seconds = Math.floor(time % 60); 54 | return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; 55 | }; 56 | 57 | return ( 58 | <> 59 |