├── .eslintrc.json ├── .github ├── FUNDING.yml └── workflows │ └── dependency-review.yml ├── .gitignore ├── .vscode └── launch.json ├── README.md ├── components.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── logo.svg ├── src ├── app │ ├── api │ │ ├── book │ │ │ └── route.ts │ │ └── passage │ │ │ └── route.ts │ ├── globals.css │ ├── layout.tsx │ ├── page.module.css │ ├── page.tsx │ └── passage │ │ └── page.tsx ├── components │ ├── Book.tsx │ └── Navbar.tsx ├── hooks │ ├── useBook.tsx │ ├── useModal.tsx │ └── usePassage.tsx └── lib │ └── utils.ts ├── tailwind.config.js └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [raselldev] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@v3 19 | - name: 'Dependency Review' 20 | uses: actions/dependency-review-action@v3 21 | -------------------------------------------------------------------------------- /.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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | .vscode/* 39 | !.vscode/settings.json 40 | !.vscode/tasks.json 41 | !.vscode/launch.json 42 | !.vscode/extensions.json 43 | !.vscode/*.code-snippets 44 | 45 | # Local History for Visual Studio Code 46 | .history/ 47 | 48 | # Built Visual Studio Code Extensions 49 | *.vsix 50 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "chrome", 5 | "name": "http://localhost:3000/pages/api/passage?passage=mat+1", 6 | "request": "launch", 7 | "url": "http://localhost:3000/pages/api/passage?passage=mat+1" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |  4 | ## Description 5 | 6 |    7 | 8 | This is a simple Bible API that provides access to various Bible passages and verses. The API is designed to allow users to retrieve Bible passages based on book and chapter 9 | 10 | ## Last Update (19-11-23) 11 | 12 | - Fetching result of Passage more simple 13 | - Adding UI 14 | - Removing theme "system" 15 | 16 | ## Features 17 | 18 | - [x] Fetch Bible passages based on book and chapter 19 | - [x] Retrieve specific verses within a passage 20 | - [x] Adding UI for sample (NEW UPDATE) 21 | - [ ] Support for multiple Bible versions 22 | 23 | ## Getting Started 24 | 25 | 1. Clone the repository: 26 | 27 | ```sh 28 | git clone https://github.com/raselldev/alkitab-api.git 29 | 30 | 1. Install some package: 31 | 32 | ```sh 33 | npm install 34 | 1. Run: 35 | 36 | ```sh 37 | npm run dev 38 | 39 | ## Endpoints 40 | | API | ENDPOINT | 41 | |--|--| 42 | | Get Book Data | api/book | 43 | | Get Passage | api/passage?passage={passage}&num={chapter} | 44 | 45 | 46 | 47 | ## Contribution 48 | 49 | Interested in enhancing this project? You can actively contribute to it. I am fully receptive to any contributions that can help improve the project. 50 | 51 | ## About the Data 52 | All bible data is from SABDA.ORG 53 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alkitab-api", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-dialog": "^1.0.5", 13 | "@radix-ui/react-dropdown-menu": "^2.0.6", 14 | "@radix-ui/react-slot": "^1.0.2", 15 | "class-variance-authority": "^0.7.0", 16 | "classnames": "^2.3.2", 17 | "clsx": "^2.0.0", 18 | "lucide-react": "^0.292.0", 19 | "next": "^14.0.1", 20 | "react": "^18", 21 | "react-dom": "^18", 22 | "tailwind-merge": "^2.0.0", 23 | "tailwindcss-animate": "^1.0.7", 24 | "xml2js": "^0.6.2" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^20", 28 | "@types/react": "^18.2.34", 29 | "@types/react-dom": "^18", 30 | "@types/xml2js": "^0.4.13", 31 | "autoprefixer": "^10.4.16", 32 | "daisyui": "^4.12.12", 33 | "eslint": "^8", 34 | "eslint-config-next": "14.0.0", 35 | "postcss": "^8.4.31", 36 | "tailwindcss": "^3.3.5", 37 | "typescript": "^5" 38 | }, 39 | "description": "## Description   ", 40 | "main": "next.config.js", 41 | "author": "raselldev", 42 | "license": "ISC" 43 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/app/api/book/route.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The above function is an asynchronous function that returns a list of books of the Bible with their 3 | * abbreviations, names, and chapter counts. 4 | * @returns a Response object with the JSON stringified version of the "result" object. 5 | */ 6 | export async function GET() { 7 | let result = { 8 | data: [ 9 | { 10 | id: 1, 11 | abbr: "Kej", 12 | name: "Kejadian", 13 | chapter: 50, 14 | }, 15 | { 16 | id: 2, 17 | abbr: "Kel", 18 | name: "Keluaran", 19 | chapter: 40, 20 | }, 21 | { 22 | id: 3, 23 | abbr: "Ima", 24 | name: "Imamat", 25 | chapter: 27, 26 | }, 27 | { 28 | id: 4, 29 | abbr: "Bil", 30 | name: "Bilangan", 31 | chapter: 36, 32 | }, 33 | { 34 | id: 5, 35 | abbr: "Ula", 36 | name: "Ulangan", 37 | chapter: 34, 38 | }, 39 | { 40 | id: 6, 41 | abbr: "Yos", 42 | name: "Yosua", 43 | chapter: 24, 44 | }, 45 | { 46 | id: 7, 47 | abbr: "Hak", 48 | name: "Hakim-hakim", 49 | chapter: 21, 50 | }, 51 | { 52 | id: 8, 53 | abbr: "Rut", 54 | name: "Rut", 55 | chapter: 4, 56 | }, 57 | { 58 | id: 9, 59 | abbr: "1 Sam", 60 | name: "1 Samuel", 61 | chapter: 31, 62 | }, 63 | { 64 | id: 10, 65 | abbr: "2 Sam", 66 | name: "2 Samuel", 67 | chapter: 24, 68 | }, 69 | { 70 | id: 11, 71 | abbr: "1 Raj", 72 | name: "1 Raja-Raja", 73 | chapter: 22, 74 | }, 75 | { 76 | id: 12, 77 | abbr: "2 Raj", 78 | name: "2 Raja-Raja", 79 | chapter: 25, 80 | }, 81 | { 82 | id: 13, 83 | abbr: "1 Taw", 84 | name: "1 Tawarikh", 85 | chapter: 29, 86 | }, 87 | { 88 | id: 14, 89 | abbr: "2 Taw", 90 | name: "2 Tawarikh", 91 | chapter: 36, 92 | }, 93 | { 94 | id: 15, 95 | abbr: "Ezr", 96 | name: "Ezra", 97 | chapter: 10, 98 | }, 99 | { 100 | id: 16, 101 | abbr: "Neh", 102 | name: "Nehemia", 103 | chapter: 13, 104 | }, 105 | { 106 | id: 17, 107 | abbr: "Est", 108 | name: "Ester", 109 | chapter: 10, 110 | }, 111 | { 112 | id: 18, 113 | abbr: "Ayb", 114 | name: "Ayub", 115 | chapter: 42, 116 | }, 117 | { 118 | id: 19, 119 | abbr: "Maz", 120 | name: "Mazmur", 121 | chapter: 150, 122 | }, 123 | { 124 | id: 20, 125 | abbr: "Ams", 126 | name: "Amsal", 127 | chapter: 31, 128 | }, 129 | { 130 | id: 21, 131 | abbr: "Pkh", 132 | name: "Pengkhotbah", 133 | chapter: 12, 134 | }, 135 | { 136 | id: 22, 137 | abbr: "Kid", 138 | name: "Kidung Agung", 139 | chapter: 8, 140 | }, 141 | { 142 | id: 23, 143 | abbr: "Yes", 144 | name: "Yesaya", 145 | chapter: 66, 146 | }, 147 | { 148 | id: 24, 149 | abbr: "Yer", 150 | name: "Yeremia", 151 | chapter: 52, 152 | }, 153 | { 154 | id: 25, 155 | abbr: "Rat", 156 | name: "Ratapan", 157 | chapter: 5, 158 | }, 159 | { 160 | id: 26, 161 | abbr: "Yeh", 162 | name: "Yehezkiel", 163 | chapter: 48, 164 | }, 165 | { 166 | id: 27, 167 | abbr: "Dan", 168 | name: "Daniel", 169 | chapter: 12, 170 | }, 171 | { 172 | id: 28, 173 | abbr: "Hos", 174 | name: "Hosea", 175 | chapter: 14, 176 | }, 177 | { 178 | id: 29, 179 | abbr: "Yoe", 180 | name: "Yoel", 181 | chapter: 3, 182 | }, 183 | { 184 | id: 30, 185 | abbr: "Amo", 186 | name: "Amos", 187 | chapter: 9, 188 | }, 189 | { 190 | id: 31, 191 | abbr: "Oba", 192 | name: "Obaja", 193 | chapter: 1, 194 | }, 195 | { 196 | id: 32, 197 | abbr: "Yun", 198 | name: "Yunus", 199 | chapter: 4, 200 | }, 201 | { 202 | id: 33, 203 | abbr: "Mik", 204 | name: "Mikha", 205 | chapter: 7, 206 | }, 207 | { 208 | id: 34, 209 | abbr: "Nah", 210 | name: "Nahum", 211 | chapter: 3, 212 | }, 213 | { 214 | id: 35, 215 | abbr: "Hab", 216 | name: "Habakuk", 217 | chapter: 3, 218 | }, 219 | { 220 | id: 36, 221 | abbr: "Zef", 222 | name: "Zefanya", 223 | chapter: 3, 224 | }, 225 | { 226 | id: 37, 227 | abbr: "Hag", 228 | name: "Hagai", 229 | chapter: 2, 230 | }, 231 | { 232 | id: 38, 233 | abbr: "Zak", 234 | name: "Zakharia", 235 | chapter: 14, 236 | }, 237 | { 238 | id: 39, 239 | abbr: "Mal", 240 | name: "Maleakhi", 241 | chapter: 4, 242 | }, 243 | { 244 | id: 40, 245 | abbr: "Mat", 246 | name: "Matius", 247 | chapter: 28, 248 | }, 249 | { 250 | id: 41, 251 | abbr: "Mar", 252 | name: "Markus", 253 | chapter: 16, 254 | }, 255 | { 256 | id: 42, 257 | abbr: "Luk", 258 | name: "Lukas", 259 | chapter: 24, 260 | }, 261 | { 262 | id: 43, 263 | abbr: "Yoh", 264 | name: "Yohanes", 265 | chapter: 21, 266 | }, 267 | { 268 | id: 44, 269 | abbr: "Kis", 270 | name: "Kisah Para Rasul", 271 | chapter: 28, 272 | }, 273 | { 274 | id: 45, 275 | abbr: "Rom", 276 | name: "Roma", 277 | chapter: 16, 278 | }, 279 | { 280 | id: 46, 281 | abbr: "1 Kor", 282 | name: "1 Korintus", 283 | chapter: 16, 284 | }, 285 | { 286 | id: 47, 287 | abbr: "2 Kor", 288 | name: "2 Korintus", 289 | chapter: 13, 290 | }, 291 | { 292 | id: 48, 293 | abbr: "Gal", 294 | name: "Galatia", 295 | chapter: 6, 296 | }, 297 | { 298 | id: 49, 299 | abbr: "Efe", 300 | name: "Efesus", 301 | chapter: 6, 302 | }, 303 | { 304 | id: 50, 305 | abbr: "Flp", 306 | name: "Filipi", 307 | chapter: 4, 308 | }, 309 | { 310 | id: 51, 311 | abbr: "Kol", 312 | name: "Kolose", 313 | chapter: 4, 314 | }, 315 | { 316 | id: 52, 317 | abbr: "1 Tes", 318 | name: "1 Tesalonika", 319 | chapter: 5, 320 | }, 321 | { 322 | id: 53, 323 | abbr: "2 Tes", 324 | name: "2 Tesalonika", 325 | chapter: 3, 326 | }, 327 | { 328 | id: 54, 329 | abbr: "1 Tim", 330 | name: "1 Timotius", 331 | chapter: 6, 332 | }, 333 | { 334 | id: 55, 335 | abbr: "2 Tim", 336 | name: "2 Timotius", 337 | chapter: 4, 338 | }, 339 | { 340 | id: 56, 341 | abbr: "Tit", 342 | name: "Titus", 343 | chapter: 3, 344 | }, 345 | { 346 | id: 57, 347 | abbr: "Flm", 348 | name: "Filemon", 349 | chapter: 1, 350 | }, 351 | { 352 | id: 58, 353 | abbr: "Ibr", 354 | name: "Ibrani", 355 | chapter: 13, 356 | }, 357 | { 358 | id: 59, 359 | abbr: "Yak", 360 | name: "Yakobus", 361 | chapter: 5, 362 | }, 363 | { 364 | id: 60, 365 | abbr: "1 Pet", 366 | name: "1 Petrus", 367 | chapter: 5, 368 | }, 369 | { 370 | id: 61, 371 | abbr: "2 Pet", 372 | name: "2 Petrus", 373 | chapter: 3, 374 | }, 375 | { 376 | id: 62, 377 | abbr: "1 Yoh", 378 | name: "1 Yohanes", 379 | chapter: 5, 380 | }, 381 | { 382 | id: 63, 383 | abbr: "2 Yoh", 384 | name: "2 Yohanes", 385 | chapter: 1, 386 | }, 387 | { 388 | id: 64, 389 | abbr: "3 Yoh", 390 | name: "3 Yohanes", 391 | chapter: 1, 392 | }, 393 | { 394 | id: 65, 395 | abbr: "Yud", 396 | name: "Yudas", 397 | chapter: 1, 398 | }, 399 | { 400 | id: 66, 401 | abbr: "Wah", 402 | name: "Wahyu", 403 | chapter: 22, 404 | }, 405 | ], 406 | }; 407 | 408 | return new Response(JSON.stringify(result)); 409 | } 410 | -------------------------------------------------------------------------------- /src/app/api/passage/route.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This TypeScript function fetches Bible passages from a specific API, parses the response, and 3 | * modifies the structure of the data for efficiency before returning it as a JSON response. 4 | * @param {NextRequest} request - The `request` parameter is an object that contains information about 5 | * the incoming HTTP request. It includes properties such as `nextUrl` which represents the URL of the 6 | * request, `headers` which contains the headers of the request, and `method` which represents the HTTP 7 | * method used for the request (in 8 | * @returns The code is returning a JSON response containing the result of parsing an XML response from 9 | * the specified URL. The XML data is fetched from the URL specified in the `url` variable, and then 10 | * parsed using the `xml2js` library. The parsed result is then modified to have a more efficient 11 | * structure, and finally converted to a JSON string before being returned as the response. 12 | */ 13 | import { NextRequest } from "next/server"; 14 | import { parseString } from "xml2js"; 15 | 16 | export async function GET(request: NextRequest) { 17 | let result: any = {}; 18 | const passage = request.nextUrl.searchParams.get("passage"); 19 | const chapter = request.nextUrl.searchParams.get("num"); 20 | const url = ` http://alkitab.sabda.org/api/passage.php?passage=${passage}${"+"}${chapter}`; 21 | 22 | try { 23 | const response = await fetch(url); 24 | const data = await response.text(); 25 | parseString(data, (error, _result) => { 26 | if (!error) { 27 | result = _result; 28 | } 29 | }); 30 | } catch (error) { 31 | console.log("error", error); 32 | } 33 | 34 | if (result && result.bible && result.bible.title && result.bible.title.length === 1) { 35 | result.bible.title = result.bible.title[0]; 36 | } 37 | 38 | // Modify the structure to make it more efficient 39 | if (result && result.bible && result.bible.book && Array.isArray(result.bible.book)) { 40 | const books = result.bible.book.map((book: any) => { 41 | return { 42 | name: book["$"].name, 43 | book_id: book.book_id[0], 44 | title: book.title[0], 45 | chapter: { 46 | chap: book.chapter[0].chap[0], 47 | verses: book.chapter[0].verses[0].verse.map((verse: any) => { 48 | return { 49 | number: verse.number[0], 50 | title: verse.title ? verse.title[0] : undefined, 51 | text: verse.text[0] 52 | }; 53 | }) 54 | } 55 | }; 56 | }); 57 | 58 | // If there is only one book, set it directly without an array 59 | result.bible.book = books.length === 1 ? books[0] : books; 60 | } 61 | 62 | const jsonString = JSON.stringify(result, null, 2); 63 | return new Response(jsonString); 64 | } 65 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Roboto } from "next/font/google" 3 | import classNames from 'classnames' 4 | import './globals.css' 5 | import { Suspense } from 'react' 6 | 7 | 8 | export const metadata: Metadata = { 9 | title: 'Alkitab API', 10 | icons: { 11 | shortcut: "/logo.svg", 12 | }, 13 | description: 'This is a simple Bible API that provides access to various Bible passages and verses.', 14 | } 15 | 16 | export default function RootLayout({ 17 | children, 18 | }: { 19 | children: React.ReactNode 20 | }) { 21 | const cn = classNames 22 | return ( 23 | 24 |
28 |Data is undefined or null.
17 | 18 | return ( 19 | <> 20 |35 | {data.bible.book.title} 36 |
37 | {data.bible.book.chapter.verses.map((v) => ( 38 |39 | {v.number}. {v.text} 40 |
41 | ))} 42 |Please Try Again
19 | 20 | return ( 21 |