{"id":1044,"date":"2026-02-12T07:24:58","date_gmt":"2026-02-12T07:24:58","guid":{"rendered":"https:\/\/www.rajeshkumar.xyz\/blog\/?p=1044"},"modified":"2026-02-12T07:25:00","modified_gmt":"2026-02-12T07:25:00","slug":"codex-master-prompt-1","status":"publish","type":"post","link":"https:\/\/www.rajeshkumar.xyz\/blog\/codex-master-prompt-1\/","title":{"rendered":"Codex Master Prompt. &#8211; 1"},"content":{"rendered":"<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Below is a **complete end-to-end Requirements Specification** you can copy\/paste into Codex (<span class=\"hljs-keyword\">or<\/span> any code generator) to build the Laravel app in one go. I kept your original requirements, filled the missing parts, <span class=\"hljs-keyword\">and<\/span> made everything **implementation-ready** (routes, modules, DB schema, validations, permissions, SEO rules, <span class=\"hljs-keyword\">and<\/span> acceptance criteria).\n\n---\n\n<span class=\"hljs-comment\">## 1) Project Overview<\/span>\n\n<span class=\"hljs-comment\">### Product name<\/span>\n\n**bestorthohospitals.com Directory App** (Laravel)\n\n<span class=\"hljs-comment\">### Purpose<\/span>\n\nA <span class=\"hljs-keyword\">global<\/span> directory listing website <span class=\"hljs-keyword\">for<\/span> **Orthopedic Hospitals** <span class=\"hljs-keyword\">and<\/span> **Orthopedic Doctors** with:\n\n* Country &amp; city based SEO URLs\n* Search\/filter on every page\n* Hospital\/Doctor profiles\n* Appointment slot management (Hospitals\/Doctors)\n* User bookings with live availability\n* Admin moderation (block\/unblock, manage content)\n* Home page aggregation (featured doctors\/hospitals + latest posts from WordPress blog + Flarum forum)\n\n<span class=\"hljs-comment\">### Primary user journeys<\/span>\n\n<span class=\"hljs-number\">1.<\/span> Visitor searches doctors\/hospitals \u2192 opens profile \u2192 logs in with Google \u2192 books a slot.\n<span class=\"hljs-number\">2.<\/span> Hospital logs in with Google \u2192 creates\/updates hospital profile \u2192 creates slots \u2192 receives bookings \u2192 approves\/cancels.\n<span class=\"hljs-number\">3.<\/span> Doctor logs in with Google \u2192 creates\/updates doctor profile \u2192 creates slots \u2192 receives bookings \u2192 approves\/cancels.\n<span class=\"hljs-number\">4.<\/span> Admin logs in with password \u2192 manages users\/profiles \u2192 blocks\/unblocks \u2192 controls featured ranking \u2192 reviews bookings.\n\n---\n\n<span class=\"hljs-comment\">## 2) Tech Stack &amp; Constraints<\/span>\n\n* PHP **<span class=\"hljs-number\">8.2<\/span>**\n* Laravel **<span class=\"hljs-number\">12<\/span>**\n* DB: **SQLite (latest)**\n* UX: **Bootstrap (latest)**\n* Auth: **Google OAuth only** <span class=\"hljs-keyword\">for<\/span> Users\/Hospitals\/Doctors\n* Admin auth: **Email\/Username + Password** (separate guard, separate URL)\n* Deployment mode: **No `\/<span class=\"hljs-keyword\">public<\/span>` in URL** (Flarum-style)\n\n---\n\n<span class=\"hljs-comment\">## 3) Top\/Bottom Menus (Global)<\/span>\n\nTop Menu &amp; Bottom Menu:\n**Home | Hospitals | Doctors | Blog | Forum | Surgery | Treatments | Diseases | Procedures | About | Contact us**\n\nRules:\n\n* \u201cBlog\u201d menu links to the WordPress blog (external).\n* \u201cForum\u201d menu links to the Flarum forum (external).\n* All other items are served from Laravel app.\n\n---\n\n<span class=\"hljs-comment\">## 4) Roles &amp; Permissions (RBAC)<\/span>\n\n<span class=\"hljs-comment\">### Roles<\/span>\n\n* **USER**: can book appointments; manage own profile basics\n* **HOSPITAL**: manage hospital profile + slots + bookings\n* **DOCTOR**: manage doctor profile + slots + bookings\n* **ADMIN**: manage everything; block\/unblock accounts; feature\/rank; view logs\/bookings\n\n<span class=\"hljs-comment\">### Permission matrix (must)<\/span>\n\n| Action                          |     User |                 Hospital |                   Doctor |       Admin |\n| ------------------------------- | -------: | -----------------------: | -----------------------: | ----------: |\n| Google login                    |        \u2705 |                        \u2705 |                        \u2705 |           \u274c |\n| Admin password login            |        \u274c |                        \u274c |                        \u274c |           \u2705 |\n| View directory                  |        \u2705 |                        \u2705 |                        \u2705 |           \u2705 |\n| Create\/update own profile       | \u2705(basic) |              \u2705(hospital) |                \u2705(doctor) |           \u2705 |\n| Create slots                    |        \u274c |                        \u2705 |                        \u2705 |           \u2705 |\n| Book slot                       |        \u2705 | \u274c(unless also USER role) | \u274c(unless also USER role) | \u2705(optional) |\n| Manage bookings <span class=\"hljs-keyword\">for<\/span> own profile |        \u274c |                        \u2705 |                        \u2705 |           \u2705 |\n| Block\/unblock accounts          |        \u274c |                        \u274c |                        \u274c |           \u2705 |\n| Manage featured\/top ranking     |        \u274c |                        \u274c |                        \u274c |           \u2705 |\n\n**Important rule:** One Google account = one primary role chosen during onboarding (USER\/HOSPITAL\/DOCTOR). Admin is separate login system.\n\n---\n\n<span class=\"hljs-comment\">## 5) Authentication Requirements<\/span>\n\n<span class=\"hljs-comment\">### Google OAuth (mandatory for non-admin)<\/span>\n\n* <span class=\"hljs-keyword\">Use<\/span> <span class=\"hljs-title\">Laravel<\/span> <span class=\"hljs-title\">Socialite<\/span> <span class=\"hljs-title\">with<\/span> <span class=\"hljs-title\">Google<\/span>.\n* <span class=\"hljs-title\">Only<\/span> <span class=\"hljs-title\">Google<\/span> <span class=\"hljs-title\">login<\/span>: **<span class=\"hljs-title\">no<\/span> <span class=\"hljs-title\">password<\/span> <span class=\"hljs-title\">login<\/span>** <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">USER<\/span>\/<span class=\"hljs-title\">HOSPITAL<\/span>\/<span class=\"hljs-title\">DOCTOR<\/span>.\n\n<span class=\"hljs-title\">Required<\/span> `.<span class=\"hljs-title\">env<\/span>` <span class=\"hljs-title\">keys<\/span> (<span class=\"hljs-title\">example<\/span>; keep in code block):\n\n```env\nAPP_URL=http:<span class=\"hljs-comment\">\/\/localhost\/bestcanow<\/span>\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\nGOOGLE_REDIRECT_URI=http:<span class=\"hljs-comment\">\/\/localhost\/bestcanow\/auth\/google\/callback<\/span>\n```\n\n<span class=\"hljs-comment\">### Onboarding after first Google login<\/span>\n\n<span class=\"hljs-keyword\">If<\/span> first-time login:\n\n<span class=\"hljs-number\">1.<\/span> Ask user to select account type: **User \/ Hospital \/ Doctor**\n<span class=\"hljs-number\">2.<\/span> Create user row with role\n<span class=\"hljs-number\">3.<\/span> Redirect to role dashboard:\n\n   * USER \u2192 \u201cMy Bookings\u201d\n   * HOSPITAL \u2192 \u201cHospital Dashboard\u201d\n   * DOCTOR \u2192 \u201cDoctor Dashboard\u201d\n\n<span class=\"hljs-comment\">### Account block\/unblock<\/span>\n\n* Admin can set `is_blocked = <span class=\"hljs-keyword\">true<\/span>`.\n* Blocked accounts cannot login (show message: \u201cAccount blocked. Contact support.\u201d).\n\n<span class=\"hljs-comment\">### Admin auth (separate)<\/span>\n\n* Separate admin guard <span class=\"hljs-keyword\">and<\/span> routes, e.g.:\n\n  * `\/admin\/login` (password)\n  * `\/admin` (dashboard)\n* Admin credentials seeded via seeder <span class=\"hljs-keyword\">or<\/span> created with CLI.\n* Admin accounts stored in `admin_users` table (separate from users).\n\n---\n\n<span class=\"hljs-comment\">## 6) Global UI Components (Must)<\/span>\n\n<span class=\"hljs-comment\">### Global Search\/Filter (Must appear at top of EVERY page)<\/span>\n\nFilter supports:\n\n* Search keyword (doctor\/hospital name)\n* Country (dropdown)\n* City (dependent dropdown <span class=\"hljs-keyword\">or<\/span> autocomplete)\n* Type toggle (Doctors \/ Hospitals \/ Both)\n* Optional filters (recommended MVP):\n\n  * Specialty tags (orthopedic sub-specialty)\n  * Treatments\/Procedures tags\n  * \u201cHas available slots\u201d checkbox\n\nBehavior:\n\n* Submitting search takes user to a combined results page <span class=\"hljs-keyword\">OR<\/span> stays within page context:\n\n  * <span class=\"hljs-keyword\">If<\/span> user is on `\/doctors`, show doctor results.\n  * <span class=\"hljs-keyword\">If<\/span> on `\/hospitals`, show hospital results.\n  * <span class=\"hljs-keyword\">If<\/span> on home, show combined results with tabs.\n\n---\n\n<span class=\"hljs-comment\">## 7) Pages &amp; Content Requirements<\/span>\n\n<span class=\"hljs-comment\">### 7.1 Home (`\/`)<\/span>\n\nSections:\n\n<span class=\"hljs-number\">1.<\/span> **Search\/Filter bar** (<span class=\"hljs-keyword\">global<\/span> component)\n<span class=\"hljs-number\">2.<\/span> **Bootstrap Carousel\/Slider**\n\n   * Admin-manageable slides (title, subtitle, CTA link)\n<span class=\"hljs-number\">3.<\/span> **Top <span class=\"hljs-number\">20<\/span> Doctors**\n<span class=\"hljs-number\">4.<\/span> **Top <span class=\"hljs-number\">20<\/span> Hospitals**\n<span class=\"hljs-number\">5.<\/span> **Top <span class=\"hljs-number\">20<\/span> Blog posts** from WordPress blog\n<span class=\"hljs-number\">6.<\/span> **Top <span class=\"hljs-number\">20<\/span> Forum posts** from Flarum forum\n\nRules <span class=\"hljs-keyword\">for<\/span> \u201cTop\u201d:\n\n* Only `published` profiles\n* Ordered by:\n\n  <span class=\"hljs-number\">1.<\/span> `is_featured DESC`\n  <span class=\"hljs-number\">2.<\/span> `featured_rank ASC` (admin-controlled)\n  <span class=\"hljs-number\">3.<\/span> `profile_views DESC` (optional)\n  <span class=\"hljs-number\">4.<\/span> `updated_at DESC`\n\n<span class=\"hljs-comment\">### 7.2 Hospitals listing (`\/hospitals`)<\/span>\n\n* Search\/Filter bar (<span class=\"hljs-keyword\">global<\/span>)\n* <span class=\"hljs-keyword\">List<\/span> **Top <span class=\"hljs-number\">30<\/span> hospitals** with pagination\n* Each item: name, city\/country, thumbnail, short summary, \u201cBook Appointment\u201d button, profile link\n\n<span class=\"hljs-comment\">### 7.3 Doctors listing (`\/doctors`)<\/span>\n\n* Search\/Filter bar (<span class=\"hljs-keyword\">global<\/span>)\n* <span class=\"hljs-keyword\">List<\/span> **Top <span class=\"hljs-number\">30<\/span> doctors** with pagination\n* Each item: name, specialization, city\/country, thumbnail, short summary, \u201cBook Appointment\u201d button, profile link\n\n<span class=\"hljs-comment\">### 7.4 Blog menu item<\/span>\n\n* Clicking \u201cBlog\u201d opens external WP blog.\n* Home page also shows \u201cTop <span class=\"hljs-number\">20<\/span> Blog posts\u201d via feed integration (see Section <span class=\"hljs-number\">12<\/span>).\n\n<span class=\"hljs-comment\">### 7.5 Forum menu item<\/span>\n\n* Clicking \u201cForum\u201d opens external Flarum forum.\n* Home page shows \u201cTop <span class=\"hljs-number\">20<\/span> Forum posts\u201d via feed integration (see Section <span class=\"hljs-number\">12<\/span>).\n\n<span class=\"hljs-comment\">### 7.6 Medical Library Pages (Surgery\/Treatments\/Diseases\/Procedures)<\/span>\n\nMVP requirement:\n\n* Each of these menu items goes to a <span class=\"hljs-keyword\">list<\/span> page:\n\n  * `\/surgery`\n  * `\/treatments`\n  * `\/diseases`\n  * `\/procedures`\n* Each item has detail page:\n\n  * `\/{type}\/{slug}`\n* Admin can create\/edit\/delete items.\n* Each detail page has SEO meta + optional related doctors\/hospitals.\n\n<span class=\"hljs-comment\">### 7.7 About (`\/about`)<\/span>\n\n<span class=\"hljs-keyword\">Static<\/span> CMS page editable by Admin.\n\n<span class=\"hljs-comment\">### 7.8 Contact (`\/contact`)<\/span>\n\n* Contact form fields: Name, Email, Subject, Message\n* Save in DB + optional email notification to site admin\n* Add anti-spam protections (minimum):\n\n  * rate limit per IP\n  * honeypot field\n\n---\n\n<span class=\"hljs-comment\">## 8) SEO &amp; Slug Requirements (Strict)<\/span>\n\n<span class=\"hljs-comment\">### 8.1 Profile URL patterns (Must)<\/span>\n\n**Doctor profile URL:**\n`\/doctors\/{country}\/{city}\/{ca-name}`\n\n**Hospital profile URL:**\n`\/hospitals\/{country}\/{city}\/{ca-name}`\n\nExamples:\n\n* `\/doctors\/india\/delhi\/doctor-name`\n* `\/hospitals\/india\/delhi\/hospital-name`\n\n<span class=\"hljs-comment\">### 8.2 Slug generation rules<\/span>\n\n* `country` = slug from country name (lowercase)\n* `city` = **city_slug** (lowercase)\n* `{ca-name}` = slug from doctor\/hospital name (lowercase)\n* Also store a **combined unique slug key** internally:\n\n  * `slug_key = <span class=\"hljs-string\">\"{country_slug}-{city_slug}-{name_slug}\"<\/span>`\n\n<span class=\"hljs-comment\">### 8.3 City normalization (Must)<\/span>\n\n* Store:\n\n  * `city_display` = Title <span class=\"hljs-keyword\">Case<\/span> (\u201c<span class=\"hljs-keyword\">New<\/span> Delhi\u201d)\n  * `city_slug` = lowercase slug (\u201c<span class=\"hljs-keyword\">new<\/span>-delhi\u201d)\n* When saving, normalize inputs to avoid duplicates like \u201cdelhi\u201d, \u201cDELHI\u201d, \u201cDelhi\u201d.\n* Search must be <span class=\"hljs-keyword\">case<\/span>-insensitive; URLs always <span class=\"hljs-keyword\">use<\/span> `<span class=\"hljs-title\">city_slug<\/span>`.\n\n### 8.4 <span class=\"hljs-title\">Top<\/span> <span class=\"hljs-title\">pages<\/span> (<span class=\"hljs-title\">Must<\/span>)\n\n<span class=\"hljs-title\">Implement<\/span> <span class=\"hljs-title\">these<\/span> <span class=\"hljs-title\">routes<\/span>:\n\n* `\/<span class=\"hljs-title\">top<\/span>-<span class=\"hljs-title\">hospitals<\/span>` \u2192 <span class=\"hljs-title\">shows<\/span> <span class=\"hljs-title\">all<\/span> <span class=\"hljs-title\">countries<\/span> (<span class=\"hljs-title\">country<\/span> <span class=\"hljs-title\">cards<\/span> <span class=\"hljs-title\">with<\/span> <span class=\"hljs-title\">counts<\/span>)\n* `\/<span class=\"hljs-title\">top<\/span>-<span class=\"hljs-title\">doctors<\/span>\/{<span class=\"hljs-title\">country<\/span>}` \u2192 <span class=\"hljs-title\">shows<\/span> 45 <span class=\"hljs-title\">doctors<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">that<\/span> <span class=\"hljs-title\">country<\/span> <span class=\"hljs-title\">in<\/span> **3 <span class=\"hljs-title\">columns<\/span> <span class=\"hljs-title\">x<\/span> 15 <span class=\"hljs-title\">rows<\/span>**\n* `\/{<span class=\"hljs-title\">doctors<\/span>|<span class=\"hljs-title\">hospitals<\/span>}\/{<span class=\"hljs-title\">country<\/span>}\/{<span class=\"hljs-title\">city<\/span>}` \u2192 <span class=\"hljs-title\">list<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">that<\/span> <span class=\"hljs-title\">city<\/span>\n\n&gt; <span class=\"hljs-title\">Even<\/span> <span class=\"hljs-title\">if<\/span> <span class=\"hljs-title\">naming<\/span> <span class=\"hljs-title\">feels<\/span> <span class=\"hljs-title\">inconsistent<\/span>, <span class=\"hljs-title\">implement<\/span> <span class=\"hljs-title\">exactly<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">above<\/span> <span class=\"hljs-title\">since<\/span> <span class=\"hljs-title\">you<\/span> <span class=\"hljs-title\">requested<\/span> <span class=\"hljs-title\">these<\/span> <span class=\"hljs-title\">patterns<\/span>.\n\n### 8.5 <span class=\"hljs-title\">SEO<\/span> <span class=\"hljs-title\">tags<\/span> (<span class=\"hljs-title\">Must<\/span> <span class=\"hljs-title\">on<\/span> <span class=\"hljs-title\">ALL<\/span> <span class=\"hljs-title\">pages<\/span>)\n\n<span class=\"hljs-title\">Every<\/span> <span class=\"hljs-title\">page<\/span> <span class=\"hljs-title\">must<\/span> <span class=\"hljs-title\">output<\/span>:\n\n* `&lt;<span class=\"hljs-title\">title<\/span>&gt;`\n* <span class=\"hljs-title\">meta<\/span> <span class=\"hljs-title\">description<\/span>\n* <span class=\"hljs-title\">meta<\/span> <span class=\"hljs-title\">keywords<\/span>\n* <span class=\"hljs-title\">canonical<\/span> <span class=\"hljs-title\">URL<\/span>\n* <span class=\"hljs-title\">Open<\/span> <span class=\"hljs-title\">Graph<\/span> <span class=\"hljs-title\">tags<\/span>\n* <span class=\"hljs-title\">Twitter<\/span> <span class=\"hljs-title\">Card<\/span> <span class=\"hljs-title\">tags<\/span>\n\n<span class=\"hljs-title\">Also<\/span> <span class=\"hljs-title\">required<\/span>:\n\n* `<span class=\"hljs-title\">robots<\/span>.<span class=\"hljs-title\">txt<\/span>`\n* `<span class=\"hljs-title\">sitemap<\/span>.<span class=\"hljs-title\">xml<\/span>` (<span class=\"hljs-title\">auto<\/span>-<span class=\"hljs-title\">generated<\/span>, <span class=\"hljs-title\">includes<\/span> <span class=\"hljs-title\">all<\/span> <span class=\"hljs-title\">public<\/span> <span class=\"hljs-title\">profiles<\/span> + <span class=\"hljs-title\">medical<\/span> <span class=\"hljs-title\">library<\/span> <span class=\"hljs-title\">pages<\/span>)\n\n---\n\n## 9) \u201c<span class=\"hljs-title\">No<\/span> <span class=\"hljs-title\">Public<\/span> <span class=\"hljs-title\">Directory<\/span>\u201d <span class=\"hljs-title\">Installation<\/span> <span class=\"hljs-title\">Requirement<\/span> (<span class=\"hljs-title\">Strict<\/span>)\n\n<span class=\"hljs-title\">Must<\/span> <span class=\"hljs-title\">run<\/span> <span class=\"hljs-title\">as<\/span>:\n\n* `<span class=\"hljs-title\">http<\/span>:\/\/<span class=\"hljs-title\">localhost<\/span>\/<span class=\"hljs-title\">bestcanow<\/span>\/` (<span class=\"hljs-title\">no<\/span> `\/<span class=\"hljs-title\">public<\/span>`)\n* <span class=\"hljs-title\">No<\/span> <span class=\"hljs-title\">vhost<\/span> <span class=\"hljs-title\">required<\/span>\n* <span class=\"hljs-title\">Root<\/span> `.<span class=\"hljs-title\">htaccess<\/span>` <span class=\"hljs-title\">handles<\/span> <span class=\"hljs-title\">routing<\/span> + <span class=\"hljs-title\">serves<\/span> <span class=\"hljs-title\">assets<\/span>\n* <span class=\"hljs-title\">Root<\/span> `<span class=\"hljs-title\">index<\/span>.<span class=\"hljs-title\">php<\/span>` <span class=\"hljs-title\">boots<\/span> <span class=\"hljs-title\">Laravel<\/span>\n\n<span class=\"hljs-title\">Acceptance<\/span> <span class=\"hljs-title\">criteria<\/span>:\n\n* <span class=\"hljs-title\">Visiting<\/span> `\/` <span class=\"hljs-title\">loads<\/span> <span class=\"hljs-title\">home<\/span> <span class=\"hljs-title\">page<\/span>\n* <span class=\"hljs-title\">Visiting<\/span> `\/<span class=\"hljs-title\">css<\/span>\/<span class=\"hljs-title\">app<\/span>.<span class=\"hljs-title\">css<\/span>` (<span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">compiled<\/span> <span class=\"hljs-title\">assets<\/span> <span class=\"hljs-title\">path<\/span>) <span class=\"hljs-title\">works<\/span>\n* <span class=\"hljs-title\">Visiting<\/span> <span class=\"hljs-title\">any<\/span> <span class=\"hljs-title\">route<\/span> <span class=\"hljs-title\">works<\/span> <span class=\"hljs-title\">without<\/span> `\/<span class=\"hljs-title\">public<\/span>`\n\n(<span class=\"hljs-title\">Implementation<\/span> <span class=\"hljs-title\">may<\/span> <span class=\"hljs-title\">either<\/span> <span class=\"hljs-title\">move<\/span> <span class=\"hljs-title\">public<\/span> <span class=\"hljs-title\">assets<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">root<\/span> <span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">forward<\/span> <span class=\"hljs-title\">requests<\/span> <span class=\"hljs-title\">to<\/span> `<span class=\"hljs-title\">public<\/span>\/` <span class=\"hljs-title\">while<\/span> <span class=\"hljs-title\">keeping<\/span> <span class=\"hljs-title\">URL<\/span> <span class=\"hljs-title\">root<\/span> <span class=\"hljs-title\">clean<\/span>\u2014<span class=\"hljs-title\">your<\/span> <span class=\"hljs-title\">spec<\/span> <span class=\"hljs-title\">requires<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">final<\/span> <span class=\"hljs-title\">behavior<\/span>, <span class=\"hljs-title\">not<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">specific<\/span> <span class=\"hljs-title\">internal<\/span> <span class=\"hljs-title\">technique<\/span>.)\n\n---\n\n## 10) <span class=\"hljs-title\">Profiles<\/span> (<span class=\"hljs-title\">Hospitals<\/span> &amp; <span class=\"hljs-title\">Doctors<\/span>)\n\n### 10.1 <span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">Profile<\/span> <span class=\"hljs-title\">Fields<\/span> (<span class=\"hljs-title\">MVP<\/span> + <span class=\"hljs-title\">validation<\/span>-<span class=\"hljs-title\">ready<\/span>)\n\n<span class=\"hljs-title\">Required<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n* <span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">name<\/span>\n* <span class=\"hljs-title\">About<\/span>\/<span class=\"hljs-title\">Description<\/span> (<span class=\"hljs-title\">rich<\/span> <span class=\"hljs-title\">text<\/span> <span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Country<\/span>\n* <span class=\"hljs-title\">City<\/span> (<span class=\"hljs-title\">city_display<\/span> + <span class=\"hljs-title\">city_slug<\/span>)\n* <span class=\"hljs-title\">Full<\/span> <span class=\"hljs-title\">address<\/span>\n* <span class=\"hljs-title\">Phone<\/span>\n* <span class=\"hljs-title\">Email<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Website<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* 1\u20132 <span class=\"hljs-title\">photos<\/span> (<span class=\"hljs-title\">upload<\/span>)\n\n<span class=\"hljs-title\">Recommended<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n* <span class=\"hljs-title\">Latitude<\/span>\/<span class=\"hljs-title\">Longitude<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Services<\/span>\/<span class=\"hljs-title\">Treatments<\/span> <span class=\"hljs-title\">tags<\/span>\n* <span class=\"hljs-title\">Working<\/span> <span class=\"hljs-title\">hours<\/span> <span class=\"hljs-title\">text<\/span>\n* <span class=\"hljs-title\">Emergency<\/span> <span class=\"hljs-title\">available<\/span> (<span class=\"hljs-title\">yes<\/span>\/<span class=\"hljs-title\">no<\/span>)\n\n### 10.2 <span class=\"hljs-title\">Doctor<\/span> <span class=\"hljs-title\">Profile<\/span> <span class=\"hljs-title\">Fields<\/span> (<span class=\"hljs-title\">MVP<\/span> + <span class=\"hljs-title\">validation<\/span>-<span class=\"hljs-title\">ready<\/span>)\n\n<span class=\"hljs-title\">Required<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n* <span class=\"hljs-title\">Doctor<\/span> <span class=\"hljs-title\">full<\/span> <span class=\"hljs-title\">name<\/span>\n* <span class=\"hljs-title\">About<\/span>\/<span class=\"hljs-title\">Description<\/span>\n* <span class=\"hljs-title\">Country<\/span>\n* <span class=\"hljs-title\">City<\/span> (<span class=\"hljs-title\">city_display<\/span> + <span class=\"hljs-title\">city_slug<\/span>)\n* <span class=\"hljs-title\">Clinic<\/span>\/<span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">association<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Specialization<\/span> (<span class=\"hljs-title\">orthopedics<\/span> + <span class=\"hljs-title\">subspecialty<\/span>)\n* <span class=\"hljs-title\">Experience<\/span> <span class=\"hljs-title\">years<\/span> (<span class=\"hljs-title\">number<\/span>)\n* <span class=\"hljs-title\">Phone<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Email<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* 1\u20132 <span class=\"hljs-title\">photos<\/span> (<span class=\"hljs-title\">upload<\/span>)\n\n<span class=\"hljs-title\">Recommended<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n* <span class=\"hljs-title\">Degrees<\/span>\/<span class=\"hljs-title\">Qualifications<\/span>\n* <span class=\"hljs-title\">Languages<\/span> <span class=\"hljs-title\">spoken<\/span>\n* <span class=\"hljs-title\">Consultation<\/span> <span class=\"hljs-title\">fee<\/span> (<span class=\"hljs-title\">optional<\/span>)\n\n### 10.3 <span class=\"hljs-title\">Profile<\/span> <span class=\"hljs-title\">publishing<\/span> <span class=\"hljs-title\">workflow<\/span>\n\n* <span class=\"hljs-title\">New<\/span> <span class=\"hljs-title\">profiles<\/span> <span class=\"hljs-title\">start<\/span> <span class=\"hljs-title\">as<\/span> `<span class=\"hljs-title\">draft<\/span>`.\n* <span class=\"hljs-title\">Owner<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">submit<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">publishing<\/span> \u2192 <span class=\"hljs-title\">becomes<\/span> `<span class=\"hljs-title\">pending_review<\/span>`.\n* <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">approve<\/span> \u2192 `<span class=\"hljs-title\">published<\/span>` <span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">reject<\/span> \u2192 `<span class=\"hljs-title\">draft<\/span>` <span class=\"hljs-title\">with<\/span> <span class=\"hljs-title\">reason<\/span>.\n\n---\n\n## 11) <span class=\"hljs-title\">Appointment<\/span> <span class=\"hljs-title\">Booking<\/span> &amp; <span class=\"hljs-title\">Slot<\/span> <span class=\"hljs-title\">System<\/span> (<span class=\"hljs-title\">Core<\/span> <span class=\"hljs-title\">Feature<\/span>)\n\n### 11.1 <span class=\"hljs-title\">Core<\/span> <span class=\"hljs-title\">concepts<\/span>\n\n* **<span class=\"hljs-title\">Slot<\/span>**: <span class=\"hljs-title\">A<\/span> <span class=\"hljs-title\">time<\/span> <span class=\"hljs-title\">window<\/span> <span class=\"hljs-title\">available<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">booking<\/span> (<span class=\"hljs-title\">date<\/span> + <span class=\"hljs-title\">start_time<\/span> + <span class=\"hljs-title\">end_time<\/span>).\n* <span class=\"hljs-title\">Slots<\/span> <span class=\"hljs-title\">belong<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">either<\/span>:\n\n  * <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">profile<\/span>, <span class=\"hljs-title\">or<\/span>\n  * <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">Doctor<\/span> <span class=\"hljs-title\">profile<\/span>\n* **<span class=\"hljs-title\">Booking<\/span>**: <span class=\"hljs-title\">A<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">reserves<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">slot<\/span>.\n\n### 11.2 <span class=\"hljs-title\">Slot<\/span> <span class=\"hljs-title\">management<\/span> (<span class=\"hljs-title\">Hospital<\/span>\/<span class=\"hljs-title\">Doctor<\/span>)\n\n<span class=\"hljs-title\">Must<\/span> <span class=\"hljs-title\">allow<\/span>:\n\n* <span class=\"hljs-title\">Create<\/span> <span class=\"hljs-title\">slots<\/span>:\n\n  * <span class=\"hljs-title\">single<\/span> <span class=\"hljs-title\">slot<\/span>\n  * <span class=\"hljs-title\">bulk<\/span> <span class=\"hljs-title\">create<\/span> (<span class=\"hljs-title\">e<\/span>.<span class=\"hljs-title\">g<\/span>., \u201c<span class=\"hljs-title\">every<\/span> <span class=\"hljs-title\">day<\/span> 10:00\u201313:00 <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">next<\/span> 14 <span class=\"hljs-title\">days<\/span>\u201d)\n* <span class=\"hljs-title\">Edit<\/span> <span class=\"hljs-title\">future<\/span> <span class=\"hljs-title\">slots<\/span>\n* <span class=\"hljs-title\">Delete<\/span> <span class=\"hljs-title\">future<\/span> <span class=\"hljs-title\">slots<\/span> (<span class=\"hljs-title\">if<\/span> <span class=\"hljs-title\">not<\/span> <span class=\"hljs-title\">booked<\/span>)\n* <span class=\"hljs-title\">View<\/span> <span class=\"hljs-title\">slot<\/span> <span class=\"hljs-title\">calendar<\/span>\/<span class=\"hljs-title\">list<\/span>\n\n<span class=\"hljs-title\">Constraints<\/span>:\n\n* <span class=\"hljs-title\">No<\/span> <span class=\"hljs-title\">overlapping<\/span> <span class=\"hljs-title\">slots<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">same<\/span> <span class=\"hljs-title\">owner<\/span> (<span class=\"hljs-title\">doctor<\/span>\/<span class=\"hljs-title\">hospital<\/span>)\n* <span class=\"hljs-title\">Slot<\/span> <span class=\"hljs-title\">duration<\/span> <span class=\"hljs-title\">min<\/span> 10 <span class=\"hljs-title\">mins<\/span>, <span class=\"hljs-title\">max<\/span> 4 <span class=\"hljs-title\">hours<\/span> (<span class=\"hljs-title\">configurable<\/span>)\n\n### 11.3 <span class=\"hljs-title\">Booking<\/span> <span class=\"hljs-title\">flow<\/span> (<span class=\"hljs-title\">User<\/span>)\n\n* <span class=\"hljs-title\">On<\/span> <span class=\"hljs-title\">profile<\/span> <span class=\"hljs-title\">page<\/span>: <span class=\"hljs-title\">show<\/span> <span class=\"hljs-title\">available<\/span> <span class=\"hljs-title\">dates<\/span>\/<span class=\"hljs-title\">slots<\/span>\n* <span class=\"hljs-title\">User<\/span> <span class=\"hljs-title\">chooses<\/span> <span class=\"hljs-title\">slot<\/span> \u2192 <span class=\"hljs-title\">must<\/span> <span class=\"hljs-title\">be<\/span> <span class=\"hljs-title\">logged<\/span> <span class=\"hljs-title\">in<\/span> (<span class=\"hljs-title\">Google<\/span>)\n* <span class=\"hljs-title\">Booking<\/span> <span class=\"hljs-title\">form<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n  * <span class=\"hljs-title\">Patient<\/span> <span class=\"hljs-title\">name<\/span> (<span class=\"hljs-title\">default<\/span> <span class=\"hljs-title\">from<\/span> <span class=\"hljs-title\">Google<\/span> <span class=\"hljs-title\">profile<\/span>, <span class=\"hljs-title\">editable<\/span>)\n  * <span class=\"hljs-title\">Email<\/span> (<span class=\"hljs-title\">from<\/span> <span class=\"hljs-title\">Google<\/span>, <span class=\"hljs-title\">read<\/span>-<span class=\"hljs-title\">only<\/span>)\n  * <span class=\"hljs-title\">Phone<\/span> (<span class=\"hljs-title\">required<\/span>)\n  * <span class=\"hljs-title\">Reason<\/span>\/<span class=\"hljs-title\">Notes<\/span> (<span class=\"hljs-title\">optional<\/span>)\n* <span class=\"hljs-title\">Booking<\/span> <span class=\"hljs-title\">statuses<\/span>:\n\n  * `<span class=\"hljs-title\">pending<\/span>` (<span class=\"hljs-title\">default<\/span>)\n  * `<span class=\"hljs-title\">confirmed<\/span>` (<span class=\"hljs-title\">owner<\/span> <span class=\"hljs-title\">confirms<\/span>)\n  * `<span class=\"hljs-title\">cancelled_by_user<\/span>`\n  * `<span class=\"hljs-title\">cancelled_by_owner<\/span>`\n  * `<span class=\"hljs-title\">completed<\/span>` (<span class=\"hljs-title\">optional<\/span>)\n\n<span class=\"hljs-title\">Rules<\/span>:\n\n* <span class=\"hljs-title\">One<\/span> <span class=\"hljs-title\">slot<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">be<\/span> <span class=\"hljs-title\">booked<\/span> <span class=\"hljs-title\">by<\/span> <span class=\"hljs-title\">only<\/span> <span class=\"hljs-title\">one<\/span> <span class=\"hljs-title\">user<\/span>.\n* <span class=\"hljs-title\">Prevent<\/span> <span class=\"hljs-title\">double<\/span>-<span class=\"hljs-title\">book<\/span> <span class=\"hljs-title\">by<\/span> <span class=\"hljs-title\">DB<\/span> <span class=\"hljs-title\">unique<\/span> <span class=\"hljs-title\">constraints<\/span> + <span class=\"hljs-title\">transaction<\/span> <span class=\"hljs-title\">lock<\/span>.\n* <span class=\"hljs-title\">User<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">cancel<\/span> <span class=\"hljs-title\">up<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">configurable<\/span> <span class=\"hljs-title\">cutoff<\/span> (<span class=\"hljs-title\">e<\/span>.<span class=\"hljs-title\">g<\/span>. 2 <span class=\"hljs-title\">hours<\/span> <span class=\"hljs-title\">before<\/span> <span class=\"hljs-title\">start<\/span>).\n\n### 11.4 <span class=\"hljs-title\">Notifications<\/span> (<span class=\"hljs-title\">MVP<\/span>)\n\n<span class=\"hljs-title\">At<\/span> <span class=\"hljs-title\">minimum<\/span>:\n\n* <span class=\"hljs-title\">Show<\/span> <span class=\"hljs-title\">dashboard<\/span> <span class=\"hljs-title\">notifications<\/span> (<span class=\"hljs-title\">in<\/span>-<span class=\"hljs-title\">app<\/span>)\n  <span class=\"hljs-title\">Recommended<\/span> (<span class=\"hljs-title\">if<\/span> <span class=\"hljs-title\">possible<\/span>):\n* <span class=\"hljs-title\">Email<\/span> <span class=\"hljs-title\">notification<\/span> <span class=\"hljs-title\">on<\/span> <span class=\"hljs-title\">booking<\/span> <span class=\"hljs-title\">create<\/span>\/<span class=\"hljs-title\">confirm<\/span>\/<span class=\"hljs-title\">cancel<\/span>\n\n### 11.5 <span class=\"hljs-title\">Timezone<\/span>\n\n* <span class=\"hljs-title\">Store<\/span> <span class=\"hljs-title\">slot<\/span> <span class=\"hljs-title\">times<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">UTC<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">DB<\/span>\n* <span class=\"hljs-title\">Display<\/span> <span class=\"hljs-title\">using<\/span>:\n\n  * <span class=\"hljs-title\">profile<\/span> <span class=\"hljs-title\">timezone<\/span> (<span class=\"hljs-title\">default<\/span> <span class=\"hljs-title\">from<\/span> <span class=\"hljs-title\">country<\/span>) <span class=\"hljs-title\">OR<\/span>\n  * <span class=\"hljs-title\">site<\/span> <span class=\"hljs-title\">timezone<\/span> <span class=\"hljs-title\">setting<\/span>\n\n---\n\n## 12) <span class=\"hljs-title\">External<\/span> <span class=\"hljs-title\">Content<\/span> <span class=\"hljs-title\">Integration<\/span> (<span class=\"hljs-title\">Home<\/span> <span class=\"hljs-title\">Page<\/span>)\n\n### 12.1 <span class=\"hljs-title\">WordPress<\/span> <span class=\"hljs-title\">blog<\/span> \u201c<span class=\"hljs-title\">Top<\/span> 20 <span class=\"hljs-title\">posts<\/span>\u201d\n\n<span class=\"hljs-title\">Use<\/span> <span class=\"hljs-title\">WordPress<\/span> <span class=\"hljs-title\">REST<\/span> <span class=\"hljs-title\">API<\/span> (<span class=\"hljs-title\">cache<\/span> <span class=\"hljs-title\">results<\/span>).\n<span class=\"hljs-title\">Example<\/span> <span class=\"hljs-title\">endpoint<\/span> <span class=\"hljs-title\">format<\/span> (<span class=\"hljs-title\">put<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">code<\/span>\/<span class=\"hljs-title\">config<\/span>; shown here <span class=\"hljs-keyword\">as<\/span> requirement):\n\n```text\nhttps:<span class=\"hljs-comment\">\/\/bestorthohospitals.com\/blog\/wp-json\/wp\/v2\/posts?per_page=20<\/span>\n```\n\nData displayed:\n\n* Title\n* Publish date\n* Featured image (<span class=\"hljs-keyword\">if<\/span> available)\n* Short excerpt\n* Link to original post (open external)\n\nCaching:\n\n* Cache <span class=\"hljs-keyword\">for<\/span> <span class=\"hljs-number\">15<\/span> minutes (configurable)\n* <span class=\"hljs-keyword\">If<\/span> API fails: show \u201cView all blog posts\u201d link only\n\n<span class=\"hljs-comment\">### 12.2 Flarum forum \u201cTop 20 posts\/discussions\u201d<\/span>\n\n<span class=\"hljs-keyword\">Use<\/span> <span class=\"hljs-title\">Flarum<\/span> <span class=\"hljs-title\">JSON<\/span>:<span class=\"hljs-title\">API<\/span> (<span class=\"hljs-title\">cache<\/span> <span class=\"hljs-title\">results<\/span>).\n<span class=\"hljs-title\">Example<\/span> <span class=\"hljs-title\">endpoint<\/span> <span class=\"hljs-title\">format<\/span>:\n\n```<span class=\"hljs-title\">text<\/span>\n<span class=\"hljs-title\">https<\/span>:\/\/<span class=\"hljs-title\">bestorthohospitals<\/span>.<span class=\"hljs-title\">com<\/span>\/<span class=\"hljs-title\">forum<\/span>\/<span class=\"hljs-title\">api<\/span>\/<span class=\"hljs-title\">discussions<\/span>?<span class=\"hljs-title\">page<\/span>&#91;<span class=\"hljs-title\">limit<\/span>]=20&amp;<span class=\"hljs-title\">sort<\/span>=-<span class=\"hljs-title\">createdAt<\/span>\n```\n\n<span class=\"hljs-title\">Data<\/span> <span class=\"hljs-title\">displayed<\/span>:\n\n* <span class=\"hljs-title\">Discussion<\/span> <span class=\"hljs-title\">title<\/span>\n* <span class=\"hljs-title\">Created<\/span> <span class=\"hljs-title\">date<\/span>\n* <span class=\"hljs-title\">Comment<\/span>\/<span class=\"hljs-title\">reply<\/span> <span class=\"hljs-title\">count<\/span> <span class=\"hljs-title\">if<\/span> <span class=\"hljs-title\">available<\/span>\n* <span class=\"hljs-title\">Link<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">original<\/span> <span class=\"hljs-title\">discussion<\/span> (<span class=\"hljs-title\">external<\/span>)\n\n<span class=\"hljs-title\">Caching<\/span>:\n\n* <span class=\"hljs-title\">Cache<\/span> <span class=\"hljs-title\">for<\/span> 15 <span class=\"hljs-title\">minutes<\/span>\n* <span class=\"hljs-title\">Failure<\/span> <span class=\"hljs-title\">fallback<\/span> <span class=\"hljs-title\">similar<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">blog<\/span>\n\n---\n\n## 13) <span class=\"hljs-title\">Database<\/span> <span class=\"hljs-title\">Schema<\/span> (<span class=\"hljs-title\">SQLite<\/span>)\n\n### 13.1 <span class=\"hljs-title\">Tables<\/span> (<span class=\"hljs-title\">minimum<\/span>)\n\n**<span class=\"hljs-title\">users<\/span>**\n\n* <span class=\"hljs-title\">id<\/span> (<span class=\"hljs-title\">pk<\/span>)\n* <span class=\"hljs-title\">google_id<\/span> (<span class=\"hljs-title\">unique<\/span>)\n* <span class=\"hljs-title\">name<\/span>\n* <span class=\"hljs-title\">email<\/span> (<span class=\"hljs-title\">unique<\/span>)\n* <span class=\"hljs-title\">avatar_url<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">role<\/span> (<span class=\"hljs-title\">enum<\/span>: <span class=\"hljs-title\">user<\/span>\/<span class=\"hljs-title\">hospital<\/span>\/<span class=\"hljs-title\">doctor<\/span>)\n* <span class=\"hljs-title\">is_blocked<\/span> (<span class=\"hljs-title\">bool<\/span> <span class=\"hljs-title\">default<\/span> <span class=\"hljs-title\">false<\/span>)\n* <span class=\"hljs-title\">last_login_at<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">admin_users<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">username<\/span>\/<span class=\"hljs-title\">email<\/span> (<span class=\"hljs-title\">unique<\/span>)\n* <span class=\"hljs-title\">password_hash<\/span>\n* <span class=\"hljs-title\">is_active<\/span>\n* <span class=\"hljs-title\">last_login_at<\/span>\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">hospital_profiles<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">user_id<\/span> (<span class=\"hljs-title\">unique<\/span> <span class=\"hljs-title\">FK<\/span> <span class=\"hljs-title\">users<\/span>)\n* <span class=\"hljs-title\">name<\/span>\n* <span class=\"hljs-title\">name_slug<\/span>\n* <span class=\"hljs-title\">slug_key<\/span> (<span class=\"hljs-title\">unique<\/span>)\n* <span class=\"hljs-title\">country_name<\/span>\n* <span class=\"hljs-title\">country_slug<\/span>\n* <span class=\"hljs-title\">city_display<\/span>\n* <span class=\"hljs-title\">city_slug<\/span>\n* <span class=\"hljs-title\">address<\/span>\n* <span class=\"hljs-title\">description<\/span>\n* <span class=\"hljs-title\">phone<\/span>\n* <span class=\"hljs-title\">email<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">website<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">photo1_path<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">photo2_path<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">status<\/span> (<span class=\"hljs-title\">draft<\/span>\/<span class=\"hljs-title\">pending_review<\/span>\/<span class=\"hljs-title\">published<\/span>)\n* <span class=\"hljs-title\">is_featured<\/span> (<span class=\"hljs-title\">bool<\/span>)\n* <span class=\"hljs-title\">featured_rank<\/span> (<span class=\"hljs-title\">int<\/span> <span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">profile_views<\/span> (<span class=\"hljs-title\">int<\/span> <span class=\"hljs-title\">default<\/span> 0)\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">doctor_profiles<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">user_id<\/span> (<span class=\"hljs-title\">unique<\/span> <span class=\"hljs-title\">FK<\/span> <span class=\"hljs-title\">users<\/span>)\n* <span class=\"hljs-title\">full_name<\/span>\n* <span class=\"hljs-title\">name_slug<\/span>\n* <span class=\"hljs-title\">slug_key<\/span> (<span class=\"hljs-title\">unique<\/span>)\n* <span class=\"hljs-title\">country_name<\/span>\n* <span class=\"hljs-title\">country_slug<\/span>\n* <span class=\"hljs-title\">city_display<\/span>\n* <span class=\"hljs-title\">city_slug<\/span>\n* <span class=\"hljs-title\">specialization<\/span>\n* <span class=\"hljs-title\">experience_years<\/span>\n* <span class=\"hljs-title\">description<\/span>\n* <span class=\"hljs-title\">phone<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">email<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">photo1_path<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">photo2_path<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">status<\/span> (<span class=\"hljs-title\">draft<\/span>\/<span class=\"hljs-title\">pending_review<\/span>\/<span class=\"hljs-title\">published<\/span>)\n* <span class=\"hljs-title\">is_featured<\/span>\n* <span class=\"hljs-title\">featured_rank<\/span>\n* <span class=\"hljs-title\">profile_views<\/span>\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">slots<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">owner_type<\/span> (<span class=\"hljs-title\">enum<\/span>: <span class=\"hljs-title\">doctor<\/span>\/<span class=\"hljs-title\">hospital<\/span>)\n* <span class=\"hljs-title\">owner_id<\/span> (<span class=\"hljs-title\">doctor_profiles<\/span>.<span class=\"hljs-title\">id<\/span> <span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">hospital_profiles<\/span>.<span class=\"hljs-title\">id<\/span>)\n* <span class=\"hljs-title\">start_at_utc<\/span> (<span class=\"hljs-title\">datetime<\/span>)\n* <span class=\"hljs-title\">end_at_utc<\/span> (<span class=\"hljs-title\">datetime<\/span>)\n* <span class=\"hljs-title\">status<\/span> (<span class=\"hljs-title\">available<\/span>\/<span class=\"hljs-title\">booked<\/span>\/<span class=\"hljs-title\">cancelled<\/span>)\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n<span class=\"hljs-title\">Indexes<\/span>:\n\n* <span class=\"hljs-title\">unique<\/span>(<span class=\"hljs-title\">owner_type<\/span>, <span class=\"hljs-title\">owner_id<\/span>, <span class=\"hljs-title\">start_at_utc<\/span>, <span class=\"hljs-title\">end_at_utc<\/span>)\n\n**<span class=\"hljs-title\">bookings<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">slot_id<\/span> (<span class=\"hljs-title\">unique<\/span> <span class=\"hljs-title\">FK<\/span> <span class=\"hljs-title\">slots<\/span>)  \u2190 <span class=\"hljs-title\">ensures<\/span> <span class=\"hljs-title\">single<\/span> <span class=\"hljs-title\">booking<\/span> <span class=\"hljs-title\">per<\/span> <span class=\"hljs-title\">slot<\/span>\n* <span class=\"hljs-title\">user_id<\/span> (<span class=\"hljs-title\">FK<\/span> <span class=\"hljs-title\">users<\/span>)\n* <span class=\"hljs-title\">owner_type<\/span> \/ <span class=\"hljs-title\">owner_id<\/span> (<span class=\"hljs-title\">redundant<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">quick<\/span> <span class=\"hljs-title\">access<\/span>)\n* <span class=\"hljs-title\">patient_name<\/span>\n* <span class=\"hljs-title\">patient_phone<\/span>\n* <span class=\"hljs-title\">patient_email<\/span>\n* <span class=\"hljs-title\">notes<\/span> (<span class=\"hljs-title\">nullable<\/span>)\n* <span class=\"hljs-title\">status<\/span> (<span class=\"hljs-title\">pending<\/span>\/<span class=\"hljs-title\">confirmed<\/span>\/<span class=\"hljs-title\">cancelled_by_user<\/span>\/<span class=\"hljs-title\">cancelled_by_owner<\/span>\/<span class=\"hljs-title\">completed<\/span>)\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">medical_library_items<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">type<\/span> (<span class=\"hljs-title\">surgery<\/span>\/<span class=\"hljs-title\">treatment<\/span>\/<span class=\"hljs-title\">disease<\/span>\/<span class=\"hljs-title\">procedure<\/span>)\n* <span class=\"hljs-title\">title<\/span>\n* <span class=\"hljs-title\">slug<\/span> (<span class=\"hljs-title\">unique<\/span> <span class=\"hljs-title\">within<\/span> <span class=\"hljs-title\">type<\/span>)\n* <span class=\"hljs-title\">content<\/span>\n* <span class=\"hljs-title\">seo_title<\/span>, <span class=\"hljs-title\">seo_description<\/span>, <span class=\"hljs-title\">seo_keywords<\/span>\n* <span class=\"hljs-title\">status<\/span> (<span class=\"hljs-title\">draft<\/span>\/<span class=\"hljs-title\">published<\/span>)\n* <span class=\"hljs-title\">created_at<\/span>, <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">pages<\/span>** (<span class=\"hljs-title\">About<\/span>\/<span class=\"hljs-title\">Contact<\/span> <span class=\"hljs-title\">content<\/span> <span class=\"hljs-title\">blocks<\/span>)\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">key<\/span> (<span class=\"hljs-title\">about<\/span>\/<span class=\"hljs-title\">contact<\/span> <span class=\"hljs-title\">etc<\/span>) <span class=\"hljs-title\">unique<\/span>\n* <span class=\"hljs-title\">title<\/span>\n* <span class=\"hljs-title\">content<\/span>\n* <span class=\"hljs-title\">seo_title<\/span>, <span class=\"hljs-title\">seo_description<\/span>, <span class=\"hljs-title\">seo_keywords<\/span>\n* <span class=\"hljs-title\">updated_at<\/span>\n\n**<span class=\"hljs-title\">contact_messages<\/span>**\n\n* <span class=\"hljs-title\">id<\/span>\n* <span class=\"hljs-title\">name<\/span>\n* <span class=\"hljs-title\">email<\/span>\n* <span class=\"hljs-title\">subject<\/span>\n* <span class=\"hljs-title\">message<\/span>\n* <span class=\"hljs-title\">created_at<\/span>\n\n---\n\n## 14) <span class=\"hljs-title\">Validation<\/span> <span class=\"hljs-title\">Rules<\/span> (<span class=\"hljs-title\">Must<\/span>)\n\n### 14.1 <span class=\"hljs-title\">Shared<\/span> <span class=\"hljs-title\">validation<\/span>\n\n* <span class=\"hljs-title\">All<\/span> <span class=\"hljs-title\">slugs<\/span>: <span class=\"hljs-title\">lowercase<\/span>, <span class=\"hljs-title\">hyphen<\/span>-<span class=\"hljs-title\">separated<\/span>, <span class=\"hljs-title\">max<\/span> 120 <span class=\"hljs-title\">chars<\/span>\n* <span class=\"hljs-title\">City<\/span> <span class=\"hljs-title\">normalization<\/span> <span class=\"hljs-title\">required<\/span>\n* <span class=\"hljs-title\">Country<\/span> <span class=\"hljs-title\">required<\/span> <span class=\"hljs-title\">from<\/span> <span class=\"hljs-title\">configured<\/span> <span class=\"hljs-title\">list<\/span>\n\n### 14.2 <span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">profile<\/span> <span class=\"hljs-title\">validation<\/span>\n\n* <span class=\"hljs-title\">name<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 3, <span class=\"hljs-title\">max<\/span> 120\n* <span class=\"hljs-title\">country_name<\/span>: <span class=\"hljs-title\">required<\/span>\n* <span class=\"hljs-title\">city_display<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 2, <span class=\"hljs-title\">max<\/span> 80\n* <span class=\"hljs-title\">address<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 5, <span class=\"hljs-title\">max<\/span> 255\n* <span class=\"hljs-title\">phone<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 7, <span class=\"hljs-title\">max<\/span> 20\n* <span class=\"hljs-title\">email<\/span>: <span class=\"hljs-title\">nullable<\/span>, <span class=\"hljs-title\">valid<\/span> <span class=\"hljs-title\">email<\/span>, <span class=\"hljs-title\">max<\/span> 120\n* <span class=\"hljs-title\">website<\/span>: <span class=\"hljs-title\">nullable<\/span>, <span class=\"hljs-title\">valid<\/span> <span class=\"hljs-title\">URL<\/span>, <span class=\"hljs-title\">max<\/span> 255\n* <span class=\"hljs-title\">description<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 20\n* <span class=\"hljs-title\">photos<\/span>: <span class=\"hljs-title\">max<\/span> 2\n\n  * <span class=\"hljs-title\">types<\/span>: <span class=\"hljs-title\">jpg<\/span>\/<span class=\"hljs-title\">png<\/span>\/<span class=\"hljs-title\">webp<\/span>\n  * <span class=\"hljs-title\">max<\/span> <span class=\"hljs-title\">size<\/span>: 2<span class=\"hljs-title\">MB<\/span> <span class=\"hljs-title\">each<\/span>\n\n### 14.3 <span class=\"hljs-title\">Doctor<\/span> <span class=\"hljs-title\">profile<\/span> <span class=\"hljs-title\">validation<\/span>\n\n* <span class=\"hljs-title\">full_name<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 3, <span class=\"hljs-title\">max<\/span> 120\n* <span class=\"hljs-title\">specialization<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">max<\/span> 120\n* <span class=\"hljs-title\">experience_years<\/span>: <span class=\"hljs-title\">required<\/span> <span class=\"hljs-title\">integer<\/span>, 0\u201380\n* <span class=\"hljs-title\">country<\/span>\/<span class=\"hljs-title\">city<\/span>: <span class=\"hljs-title\">required<\/span>\n* <span class=\"hljs-title\">description<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">min<\/span> 20\n* <span class=\"hljs-title\">phone<\/span>\/<span class=\"hljs-title\">email<\/span>\/<span class=\"hljs-title\">photos<\/span> <span class=\"hljs-title\">similar<\/span> <span class=\"hljs-title\">rules<\/span>\n\n### 14.4 <span class=\"hljs-title\">Slot<\/span> <span class=\"hljs-title\">validation<\/span>\n\n* <span class=\"hljs-title\">start_at<\/span> &lt; <span class=\"hljs-title\">end_at<\/span>\n* <span class=\"hljs-title\">start_at<\/span> <span class=\"hljs-title\">must<\/span> <span class=\"hljs-title\">be<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">future<\/span> (<span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">creating<\/span>)\n* <span class=\"hljs-title\">no<\/span> <span class=\"hljs-title\">overlap<\/span> <span class=\"hljs-title\">with<\/span> <span class=\"hljs-title\">existing<\/span> <span class=\"hljs-title\">slots<\/span> (<span class=\"hljs-title\">same<\/span> <span class=\"hljs-title\">owner<\/span>)\n* <span class=\"hljs-title\">duration<\/span> 10\u2013240 <span class=\"hljs-title\">minutes<\/span> (<span class=\"hljs-title\">configurable<\/span>)\n\n### 14.5 <span class=\"hljs-title\">Booking<\/span> <span class=\"hljs-title\">validation<\/span>\n\n* <span class=\"hljs-title\">must<\/span> <span class=\"hljs-title\">be<\/span> <span class=\"hljs-title\">logged<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">USER<\/span> <span class=\"hljs-title\">role<\/span>\n* <span class=\"hljs-title\">slot<\/span> <span class=\"hljs-title\">must<\/span> <span class=\"hljs-title\">be<\/span> `<span class=\"hljs-title\">available<\/span>`\n* <span class=\"hljs-title\">patient_name<\/span>: <span class=\"hljs-title\">required<\/span>, 2\u2013120\n* <span class=\"hljs-title\">patient_phone<\/span>: <span class=\"hljs-title\">required<\/span>, 7\u201320\n* <span class=\"hljs-title\">patient_email<\/span>: <span class=\"hljs-title\">required<\/span>, <span class=\"hljs-title\">valid<\/span> <span class=\"hljs-title\">email<\/span>\n* <span class=\"hljs-title\">notes<\/span>: <span class=\"hljs-title\">optional<\/span> <span class=\"hljs-title\">max<\/span> 500\n\n---\n\n## 15) <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">Panel<\/span> <span class=\"hljs-title\">Requirements<\/span>\n\n### <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">modules<\/span>\n\n1. <span class=\"hljs-title\">Dashboard<\/span> (<span class=\"hljs-title\">counts<\/span>: <span class=\"hljs-title\">users<\/span>, <span class=\"hljs-title\">doctors<\/span>, <span class=\"hljs-title\">hospitals<\/span>, <span class=\"hljs-title\">bookings<\/span>)\n2. <span class=\"hljs-title\">Users<\/span> <span class=\"hljs-title\">management<\/span>\n\n   * <span class=\"hljs-title\">list<\/span>\/<span class=\"hljs-title\">filter<\/span> <span class=\"hljs-title\">by<\/span> <span class=\"hljs-title\">role<\/span>\/<span class=\"hljs-title\">status<\/span>\n   * <span class=\"hljs-title\">view<\/span> <span class=\"hljs-title\">details<\/span>\n   * <span class=\"hljs-title\">block<\/span>\/<span class=\"hljs-title\">unblock<\/span>\n3. <span class=\"hljs-title\">Doctors<\/span> <span class=\"hljs-title\">management<\/span>\n\n   * <span class=\"hljs-title\">approve<\/span>\/<span class=\"hljs-title\">reject<\/span> <span class=\"hljs-title\">profiles<\/span>\n   * <span class=\"hljs-title\">set<\/span> <span class=\"hljs-title\">featured<\/span> + <span class=\"hljs-title\">featured_rank<\/span>\n4. <span class=\"hljs-title\">Hospitals<\/span> <span class=\"hljs-title\">management<\/span>\n\n   * <span class=\"hljs-title\">approve<\/span>\/<span class=\"hljs-title\">reject<\/span> <span class=\"hljs-title\">profiles<\/span>\n   * <span class=\"hljs-title\">set<\/span> <span class=\"hljs-title\">featured<\/span> + <span class=\"hljs-title\">featured_rank<\/span>\n5. <span class=\"hljs-title\">Bookings<\/span> <span class=\"hljs-title\">viewer<\/span>\n\n   * <span class=\"hljs-title\">filter<\/span> <span class=\"hljs-title\">by<\/span> <span class=\"hljs-title\">status<\/span>\/<span class=\"hljs-title\">date<\/span>\/<span class=\"hljs-title\">owner<\/span>\n6. <span class=\"hljs-title\">Medical<\/span> <span class=\"hljs-title\">library<\/span> <span class=\"hljs-title\">CMS<\/span>\n\n   * <span class=\"hljs-title\">CRUD<\/span> <span class=\"hljs-title\">items<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">surgery<\/span>\/<span class=\"hljs-title\">treatments<\/span>\/<span class=\"hljs-title\">diseases<\/span>\/<span class=\"hljs-title\">procedures<\/span>\n7. <span class=\"hljs-title\">Carousel<\/span> <span class=\"hljs-title\">manager<\/span>\n\n   * <span class=\"hljs-title\">CRUD<\/span> <span class=\"hljs-title\">slides<\/span>\n\n<span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">acceptance<\/span> <span class=\"hljs-title\">criteria<\/span>:\n\n* <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">pages<\/span> <span class=\"hljs-title\">inaccessible<\/span> <span class=\"hljs-title\">without<\/span> <span class=\"hljs-title\">admin<\/span> <span class=\"hljs-title\">login<\/span>\n* <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">block<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">and<\/span> <span class=\"hljs-title\">that<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">cannot<\/span> <span class=\"hljs-title\">login<\/span> <span class=\"hljs-title\">via<\/span> <span class=\"hljs-title\">Google<\/span> <span class=\"hljs-title\">afterward<\/span>\n\n---\n\n## 16) <span class=\"hljs-title\">Performance<\/span> &amp; <span class=\"hljs-title\">Security<\/span> <span class=\"hljs-title\">Requirements<\/span>\n\n### <span class=\"hljs-title\">Performance<\/span>\n\n* <span class=\"hljs-title\">Pagination<\/span> <span class=\"hljs-title\">everywhere<\/span> (<span class=\"hljs-title\">doctors<\/span>\/<span class=\"hljs-title\">hospitals<\/span> <span class=\"hljs-title\">lists<\/span>)\n* <span class=\"hljs-title\">Cache<\/span> <span class=\"hljs-title\">external<\/span> <span class=\"hljs-title\">WP<\/span>\/<span class=\"hljs-title\">Flarum<\/span> <span class=\"hljs-title\">feeds<\/span>\n* <span class=\"hljs-title\">Index<\/span> <span class=\"hljs-title\">commonly<\/span> <span class=\"hljs-title\">filtered<\/span> <span class=\"hljs-title\">fields<\/span>:\n\n  * <span class=\"hljs-title\">country_slug<\/span>, <span class=\"hljs-title\">city_slug<\/span>, <span class=\"hljs-title\">is_featured<\/span>, <span class=\"hljs-title\">status<\/span>\n\n### <span class=\"hljs-title\">Security<\/span>\n\n* <span class=\"hljs-title\">CSRF<\/span> <span class=\"hljs-title\">protection<\/span> <span class=\"hljs-title\">enabled<\/span>\n* <span class=\"hljs-title\">Rate<\/span> <span class=\"hljs-title\">limiting<\/span>:\n\n  * <span class=\"hljs-title\">login<\/span> <span class=\"hljs-title\">attempts<\/span> (<span class=\"hljs-title\">admin<\/span>)\n  * <span class=\"hljs-title\">contact<\/span> <span class=\"hljs-title\">form<\/span> <span class=\"hljs-title\">submissions<\/span>\n  * <span class=\"hljs-title\">booking<\/span> <span class=\"hljs-title\">attempts<\/span>\n* <span class=\"hljs-title\">File<\/span> <span class=\"hljs-title\">upload<\/span> <span class=\"hljs-title\">security<\/span>:\n\n  * <span class=\"hljs-title\">restrict<\/span> <span class=\"hljs-title\">MIME<\/span> + <span class=\"hljs-title\">extension<\/span>\n  * <span class=\"hljs-title\">store<\/span> <span class=\"hljs-title\">outside<\/span> <span class=\"hljs-title\">web<\/span> <span class=\"hljs-title\">root<\/span> <span class=\"hljs-title\">via<\/span> <span class=\"hljs-title\">Laravel<\/span> <span class=\"hljs-title\">storage<\/span>\n* <span class=\"hljs-title\">Authorization<\/span> <span class=\"hljs-title\">checks<\/span>:\n\n  * <span class=\"hljs-title\">hospital<\/span> <span class=\"hljs-title\">cannot<\/span> <span class=\"hljs-title\">edit<\/span> <span class=\"hljs-title\">doctor<\/span> <span class=\"hljs-title\">profile<\/span> <span class=\"hljs-title\">and<\/span> <span class=\"hljs-title\">vice<\/span> <span class=\"hljs-title\">versa<\/span>\n  * <span class=\"hljs-title\">owners<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">only<\/span> <span class=\"hljs-title\">manage<\/span> <span class=\"hljs-title\">their<\/span> <span class=\"hljs-title\">own<\/span> <span class=\"hljs-title\">slots<\/span>\/<span class=\"hljs-title\">bookings<\/span>\n\n---\n\n## 17) <span class=\"hljs-title\">Deployment<\/span> &amp; <span class=\"hljs-title\">Environment<\/span>\n\n### <span class=\"hljs-title\">No<\/span>-<span class=\"hljs-title\">public<\/span>-<span class=\"hljs-title\">directory<\/span> <span class=\"hljs-title\">mode<\/span> <span class=\"hljs-title\">acceptance<\/span> <span class=\"hljs-title\">checklist<\/span>\n\n* <span class=\"hljs-title\">Works<\/span> <span class=\"hljs-title\">from<\/span> `<span class=\"hljs-title\">htdocs<\/span>\/<span class=\"hljs-title\">bestcanow<\/span>`\n* <span class=\"hljs-title\">No<\/span> <span class=\"hljs-title\">vhost<\/span> <span class=\"hljs-title\">needed<\/span>\n* <span class=\"hljs-title\">Root<\/span> <span class=\"hljs-title\">routing<\/span> <span class=\"hljs-title\">works<\/span>\n* <span class=\"hljs-title\">Assets<\/span> <span class=\"hljs-title\">load<\/span>\n\n### <span class=\"hljs-title\">Environment<\/span> <span class=\"hljs-title\">config<\/span>\n\n* `.<span class=\"hljs-title\">env<\/span>` <span class=\"hljs-title\">drives<\/span> <span class=\"hljs-title\">all<\/span> <span class=\"hljs-title\">secrets<\/span> (<span class=\"hljs-title\">Google<\/span> <span class=\"hljs-title\">OAuth<\/span>, <span class=\"hljs-title\">mail<\/span> <span class=\"hljs-title\">settings<\/span>, <span class=\"hljs-title\">cache<\/span> <span class=\"hljs-title\">TTLs<\/span>)\n\n---\n\n## 18) <span class=\"hljs-title\">Testing<\/span> &amp; <span class=\"hljs-title\">Acceptance<\/span> <span class=\"hljs-title\">Criteria<\/span> (<span class=\"hljs-title\">Must<\/span>)\n\n### <span class=\"hljs-title\">Automated<\/span> <span class=\"hljs-title\">tests<\/span> (<span class=\"hljs-title\">minimum<\/span>)\n\n* <span class=\"hljs-title\">Slug<\/span> <span class=\"hljs-title\">generation<\/span> <span class=\"hljs-title\">tests<\/span> (<span class=\"hljs-title\">city<\/span> <span class=\"hljs-title\">normalization<\/span>, <span class=\"hljs-title\">uniqueness<\/span>)\n* <span class=\"hljs-title\">Booking<\/span> <span class=\"hljs-title\">conflict<\/span> <span class=\"hljs-title\">test<\/span> (<span class=\"hljs-title\">double<\/span>-<span class=\"hljs-title\">book<\/span> <span class=\"hljs-title\">prevention<\/span>)\n* <span class=\"hljs-title\">Role<\/span> <span class=\"hljs-title\">authorization<\/span> <span class=\"hljs-title\">tests<\/span> (<span class=\"hljs-title\">hospital<\/span> <span class=\"hljs-title\">cannot<\/span> <span class=\"hljs-title\">book<\/span>, <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">cannot<\/span> <span class=\"hljs-title\">create<\/span> <span class=\"hljs-title\">slots<\/span>)\n* <span class=\"hljs-title\">Admin<\/span> <span class=\"hljs-title\">block<\/span>\/<span class=\"hljs-title\">unblock<\/span> <span class=\"hljs-title\">test<\/span>\n\n### <span class=\"hljs-title\">Manual<\/span> <span class=\"hljs-title\">acceptance<\/span> <span class=\"hljs-title\">tests<\/span>\n\n1. <span class=\"hljs-title\">Google<\/span> <span class=\"hljs-title\">login<\/span> <span class=\"hljs-title\">creates<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">and<\/span> <span class=\"hljs-title\">routes<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">correct<\/span> <span class=\"hljs-title\">dashboard<\/span>\n2. <span class=\"hljs-title\">Hospital<\/span> <span class=\"hljs-title\">creates<\/span> <span class=\"hljs-title\">profile<\/span> \u2192 <span class=\"hljs-title\">submits<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">review<\/span> \u2192 <span class=\"hljs-title\">admin<\/span> <span class=\"hljs-title\">publishes<\/span> \u2192 <span class=\"hljs-title\">appears<\/span> <span class=\"hljs-title\">in<\/span> <span class=\"hljs-title\">listings<\/span>\n3. <span class=\"hljs-title\">Doctor<\/span> <span class=\"hljs-title\">creates<\/span> <span class=\"hljs-title\">slots<\/span> \u2192 <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">books<\/span> <span class=\"hljs-title\">slot<\/span> \u2192 <span class=\"hljs-title\">doctor<\/span> <span class=\"hljs-title\">confirms<\/span> \u2192 <span class=\"hljs-title\">status<\/span> <span class=\"hljs-title\">updates<\/span>\n4. `\/<span class=\"hljs-title\">doctors<\/span>\/{<span class=\"hljs-title\">country<\/span>}\/{<span class=\"hljs-title\">city<\/span>}\/{<span class=\"hljs-title\">name<\/span>}` <span class=\"hljs-title\">works<\/span> <span class=\"hljs-title\">and<\/span> <span class=\"hljs-title\">SEO<\/span> <span class=\"hljs-title\">meta<\/span> <span class=\"hljs-title\">present<\/span>\n5. `\/<span class=\"hljs-title\">top<\/span>-<span class=\"hljs-title\">hospitals<\/span>` <span class=\"hljs-title\">shows<\/span> <span class=\"hljs-title\">all<\/span> <span class=\"hljs-title\">countries<\/span>\n6. `\/<span class=\"hljs-title\">top<\/span>-<span class=\"hljs-title\">doctors<\/span>\/{<span class=\"hljs-title\">country<\/span>}` <span class=\"hljs-title\">shows<\/span> 45 <span class=\"hljs-title\">entries<\/span> <span class=\"hljs-title\">in<\/span> 3<span class=\"hljs-title\">x15<\/span> <span class=\"hljs-title\">layout<\/span>\n7. <span class=\"hljs-title\">Home<\/span> <span class=\"hljs-title\">shows<\/span> <span class=\"hljs-title\">WP<\/span> + <span class=\"hljs-title\">Flarum<\/span> <span class=\"hljs-title\">items<\/span> (<span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">graceful<\/span> <span class=\"hljs-title\">fallback<\/span>)\n\n---\n\n## 19) <span class=\"hljs-title\">Recommended<\/span> \u201c<span class=\"hljs-title\">Nice<\/span>-<span class=\"hljs-title\">to<\/span>-<span class=\"hljs-title\">have<\/span>\u201d (<span class=\"hljs-title\">Optional<\/span>, <span class=\"hljs-title\">not<\/span> <span class=\"hljs-title\">required<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">MVP<\/span>)\n\n* <span class=\"hljs-title\">Reviews<\/span> &amp; <span class=\"hljs-title\">ratings<\/span>\n* <span class=\"hljs-title\">Verified<\/span> <span class=\"hljs-title\">badge<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">doctors<\/span>\/<span class=\"hljs-title\">hospitals<\/span>\n* <span class=\"hljs-title\">Multi<\/span>-<span class=\"hljs-title\">language<\/span> <span class=\"hljs-title\">SEO<\/span> <span class=\"hljs-title\">fields<\/span>\n* <span class=\"hljs-title\">Advanced<\/span> <span class=\"hljs-title\">search<\/span> (<span class=\"hljs-title\">full<\/span>-<span class=\"hljs-title\">text<\/span> <span class=\"hljs-title\">index<\/span>)\n* <span class=\"hljs-title\">Map<\/span> <span class=\"hljs-title\">view<\/span> (<span class=\"hljs-title\">lat<\/span>\/<span class=\"hljs-title\">lng<\/span>)\n\n---\n\n### <span class=\"hljs-title\">If<\/span> <span class=\"hljs-title\">you<\/span> <span class=\"hljs-title\">want<\/span>, <span class=\"hljs-title\">I<\/span> <span class=\"hljs-title\">can<\/span> <span class=\"hljs-title\">also<\/span> <span class=\"hljs-title\">produce<\/span> <span class=\"hljs-title\">a<\/span> **<span class=\"hljs-title\">single<\/span> \u201c<span class=\"hljs-title\">Codex<\/span> <span class=\"hljs-title\">Master<\/span> <span class=\"hljs-title\">Prompt<\/span>\u201d** <span class=\"hljs-title\">version<\/span> <span class=\"hljs-title\">of<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">above<\/span> (<span class=\"hljs-title\">condensed<\/span>, <span class=\"hljs-title\">no<\/span> <span class=\"hljs-title\">repetition<\/span>, <span class=\"hljs-title\">written<\/span> <span class=\"hljs-title\">like<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">build<\/span> <span class=\"hljs-title\">instruction<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">generate<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">full<\/span> <span class=\"hljs-title\">Laravel<\/span> <span class=\"hljs-title\">project<\/span> <span class=\"hljs-title\">structure<\/span> + <span class=\"hljs-title\">migrations<\/span> + <span class=\"hljs-title\">controllers<\/span> + <span class=\"hljs-title\">views<\/span>).\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1044","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/posts\/1044","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/comments?post=1044"}],"version-history":[{"count":1,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/posts\/1044\/revisions"}],"predecessor-version":[{"id":1045,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/posts\/1044\/revisions\/1045"}],"wp:attachment":[{"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/media?parent=1044"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/categories?post=1044"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rajeshkumar.xyz\/blog\/wp-json\/wp\/v2\/tags?post=1044"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}