Compare commits
14
Commits
4da7735432
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
072d7496d8 | ||
|
|
3ad6d6c0df | ||
|
|
e804e1c5e8 | ||
|
|
6709d2ef1d | ||
|
|
02925092c5 | ||
|
|
e7a5f32c77 | ||
|
|
e773537ec7 | ||
|
|
3b376babdc | ||
|
|
5d5c2ec577 | ||
|
|
43ff22687d | ||
|
|
20d035d0dc | ||
|
|
50ed3a6891 | ||
|
|
8335a1da0e | ||
|
|
e2a8390ae4 |
@@ -0,0 +1,228 @@
|
|||||||
|
# Deployment: app.mydentalofficemanagement.com
|
||||||
|
|
||||||
|
How this app is served in production: nginx serves a static production
|
||||||
|
build on this machine, and Cloudflare Tunnel exposes it publicly at
|
||||||
|
`https://app.mydentalofficemanagement.com` without opening any ports on
|
||||||
|
the router.
|
||||||
|
|
||||||
|
```
|
||||||
|
Browser → Cloudflare (DNS + edge) → Tunnel (cloudflared, systemd) → nginx (:80) → static files
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. Prerequisites
|
||||||
|
|
||||||
|
- Debian (tested on Debian 13 "trixie")
|
||||||
|
- sudo access
|
||||||
|
- Node.js + pnpm installed, repo cloned
|
||||||
|
- The domain `mydentalofficemanagement.com` already added as a zone in
|
||||||
|
Cloudflare, with a Cloudflare account you can log into
|
||||||
|
|
||||||
|
## 2. Install cloudflared
|
||||||
|
|
||||||
|
Debian's codename is usually too new for Cloudflare's apt repo, so install
|
||||||
|
the `.deb` package directly instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL -o /tmp/cloudflared.deb \
|
||||||
|
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
|
||||||
|
sudo dpkg -i /tmp/cloudflared.deb
|
||||||
|
cloudflared --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Authenticate cloudflared
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cloudflared tunnel login
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens a browser — log in and authorize the `mydentalofficemanagement.com`
|
||||||
|
zone. This writes `~/.cloudflared/cert.pem`.
|
||||||
|
|
||||||
|
## 4. Tunnel
|
||||||
|
|
||||||
|
This project uses an existing dashboard-managed tunnel named
|
||||||
|
**`mydentalmanagement`** (tunnel ID `027669d4-3d96-4f90-920d-e727cecf3b92`).
|
||||||
|
It already has other subdomains routed through it, so **reuse it** — don't
|
||||||
|
create a new tunnel for this app.
|
||||||
|
|
||||||
|
If it doesn't exist yet on a fresh Cloudflare account, create one:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cloudflared tunnel create mydentalmanagement
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get a run token
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cloudflared tunnel token mydentalmanagement
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the printed token for the next step. (This token authenticates this
|
||||||
|
machine to run the tunnel — treat it like a secret, don't commit it.)
|
||||||
|
|
||||||
|
### Install cloudflared as a service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cloudflared service install <TOKEN>
|
||||||
|
sudo systemctl enable --now cloudflared
|
||||||
|
sudo systemctl status cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Cloudflare public hostname route
|
||||||
|
|
||||||
|
In the Cloudflare dashboard:
|
||||||
|
|
||||||
|
**Zero Trust → Networks → Tunnels → mydentalmanagement → Published
|
||||||
|
application routes → Add a published application route**
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Hostname | `app.mydentalofficemanagement.com` |
|
||||||
|
| Path | *(leave empty)* |
|
||||||
|
| Service Type | `HTTP` |
|
||||||
|
| URL | `localhost:80` |
|
||||||
|
|
||||||
|
Note: `mydentalmanagement` was originally a locally-configured tunnel and
|
||||||
|
had to be **migrated** to dashboard-managed mode the first time this was
|
||||||
|
set up (Cloudflare shows a one-time "Migrate" prompt for this). That step
|
||||||
|
is already done — future public hostname routes can just be added
|
||||||
|
directly from this tab.
|
||||||
|
|
||||||
|
If you see "A DNS record with this name already exists," a stale
|
||||||
|
CNAME/A record is occupying that hostname — delete it in **DNS → Records**
|
||||||
|
first, then re-add the route (this recreates the DNS record correctly as
|
||||||
|
part of the route).
|
||||||
|
|
||||||
|
## 6. Install nginx
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get install -y nginx
|
||||||
|
sudo rm -f /etc/nginx/sites-enabled/default
|
||||||
|
```
|
||||||
|
|
||||||
|
If Apache is installed and already bound to port 80, disable it first
|
||||||
|
(only if nothing else depends on it):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl disable --now apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### nginx site config
|
||||||
|
|
||||||
|
Create `/etc/nginx/sites-available/app.mydentalofficemanagement.com.conf`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name app.mydentalofficemanagement.com;
|
||||||
|
|
||||||
|
root /var/www/app.mydentalofficemanagement.com;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ln -s /etc/nginx/sites-available/app.mydentalofficemanagement.com.conf \
|
||||||
|
/etc/nginx/sites-enabled/
|
||||||
|
sudo nginx -t && sudo systemctl reload nginx
|
||||||
|
sudo systemctl enable nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `/var/www/...` is used (not a path under `/home/<user>/...`)
|
||||||
|
because nginx runs as `www-data`, and a typical home directory
|
||||||
|
(`drwx------`) blocks that user from traversing into it.
|
||||||
|
|
||||||
|
## 7. Build and deploy the app
|
||||||
|
|
||||||
|
`vite.config.ts` requires `PORT` and `BASE_PATH` env vars to even load
|
||||||
|
(`PORT` isn't actually used by the static build, but the config throws
|
||||||
|
without it regardless of command).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "artifacts/dental-app"
|
||||||
|
PORT=5173 BASE_PATH=/ npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy the build output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /var/www/app.mydentalofficemanagement.com
|
||||||
|
sudo cp -r dist/public/* /var/www/app.mydentalofficemanagement.com/
|
||||||
|
sudo chown -R www-data:www-data /var/www/app.mydentalofficemanagement.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redeploying after code changes
|
||||||
|
|
||||||
|
Repeat the build + copy steps above any time the app changes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "artifacts/dental-app"
|
||||||
|
PORT=5173 BASE_PATH=/ npm run build
|
||||||
|
sudo cp -r dist/public/* /var/www/app.mydentalofficemanagement.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. Running the site
|
||||||
|
|
||||||
|
By default `nginx` and `cloudflared` are systemd services and can either
|
||||||
|
auto-start on boot or be started manually.
|
||||||
|
|
||||||
|
**Enable auto-start on boot:**
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable nginx
|
||||||
|
sudo systemctl enable cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
**Disable auto-start (manual start only):**
|
||||||
|
```bash
|
||||||
|
sudo systemctl disable nginx
|
||||||
|
sudo systemctl disable cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manually start the site** (needed after every reboot if auto-start is
|
||||||
|
disabled):
|
||||||
|
```bash
|
||||||
|
sudo systemctl start cloudflared
|
||||||
|
sudo systemctl start nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
**Stop the site:**
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
sudo systemctl stop cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check status:**
|
||||||
|
```bash
|
||||||
|
systemctl status nginx cloudflared
|
||||||
|
curl -I https://app.mydentalofficemanagement.com
|
||||||
|
```
|
||||||
|
|
||||||
|
No Node/Vite process is needed to serve the site — it's a static build
|
||||||
|
served directly by nginx.
|
||||||
|
|
||||||
|
## 9. Local development preview (not the public site)
|
||||||
|
|
||||||
|
To preview changes locally before building/deploying, separate from the
|
||||||
|
live public site:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd "artifacts/dental-app"
|
||||||
|
PORT=5173 BASE_PATH=/ npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `http://localhost:5173` in a browser on this machine.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **502 from the public URL**: check `sudo journalctl -u cloudflared -n 40`
|
||||||
|
for the exact error (often a port mismatch between the tunnel's
|
||||||
|
Published Application Route and what nginx is actually listening on).
|
||||||
|
- **nginx `502`/`403` locally**: check `sudo tail -n 30 /var/log/nginx/error.log`.
|
||||||
|
- **`cloudflared tunnel list` shows "no active connection"**: the
|
||||||
|
systemd service isn't running — `sudo systemctl status cloudflared`.
|
||||||
|
- **Port 80 already in use**: check `sudo ss -tlnp | grep ':80 '` to see
|
||||||
|
what's holding it (commonly Apache's default install).
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Watches src/ for changes and automatically rebuilds + redeploys the
|
||||||
|
# static site to /var/www/app.mydentalofficemanagement.com.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
DEPLOY_DIR="/var/www/app.mydentalofficemanagement.com"
|
||||||
|
|
||||||
|
build_and_deploy() {
|
||||||
|
echo "[watch-deploy] change detected, rebuilding..."
|
||||||
|
if PORT=5173 BASE_PATH=/ npm run build; then
|
||||||
|
find "${DEPLOY_DIR:?}" -mindepth 1 -delete
|
||||||
|
cp -r dist/public/* "$DEPLOY_DIR"/
|
||||||
|
echo "[watch-deploy] deployed at $(date)"
|
||||||
|
else
|
||||||
|
echo "[watch-deploy] build failed, not deploying"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_and_deploy
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
inotifywait -r -e modify,create,delete,move --exclude '(^|/)\.' src/ >/dev/null 2>&1
|
||||||
|
# debounce: wait for rapid successive saves to settle
|
||||||
|
sleep 1
|
||||||
|
while inotifywait -t 1 -r -e modify,create,delete,move --exclude '(^|/)\.' src/ >/dev/null 2>&1; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
build_and_deploy
|
||||||
|
done
|
||||||
@@ -18,8 +18,8 @@ function LandingPage() {
|
|||||||
<Navbar />
|
<Navbar />
|
||||||
<main>
|
<main>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Features />
|
|
||||||
<HowItWorks />
|
<HowItWorks />
|
||||||
|
<Features />
|
||||||
<StatsBar />
|
<StatsBar />
|
||||||
<FinalCTA />
|
<FinalCTA />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -64,14 +64,13 @@ export function Features() {
|
|||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Features 2 & 3 — two columns below, each with image */}
|
{/* Feature 2 — full-width, image on the left, text on the right */}
|
||||||
<div className="grid md:grid-cols-2 gap-8">
|
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
className="bg-white rounded-3xl shadow-sm border border-border hover:shadow-lg transition-all duration-300 group overflow-hidden flex flex-col"
|
className="bg-white rounded-3xl shadow-sm border border-border hover:shadow-lg transition-all duration-300 group overflow-hidden flex flex-col md:flex-row"
|
||||||
data-testid="feature-card-2"
|
data-testid="feature-card-2"
|
||||||
>
|
>
|
||||||
<div className="h-52 overflow-hidden">
|
<div className="md:w-1/2 h-64 md:h-auto overflow-hidden">
|
||||||
<img
|
<img
|
||||||
src={patientSrc}
|
src={patientSrc}
|
||||||
alt="Dental receptionist managing after-hours patient communication"
|
alt="Dental receptionist managing after-hours patient communication"
|
||||||
@@ -79,7 +78,7 @@ export function Features() {
|
|||||||
data-testid="img-patient-connection"
|
data-testid="img-patient-connection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-8 flex flex-col flex-1">
|
<div className="flex-1 p-8 md:p-10 flex flex-col justify-center">
|
||||||
<div className="w-14 h-14 bg-accent/20 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-accent/30 transition-colors">
|
<div className="w-14 h-14 bg-accent/20 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-accent/30 transition-colors">
|
||||||
<Clock className="w-7 h-7 text-primary" />
|
<Clock className="w-7 h-7 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
@@ -92,20 +91,13 @@ export function Features() {
|
|||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Feature 3 — full-width, text on the left, image on the right */}
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
className="bg-white rounded-3xl shadow-sm border border-border hover:shadow-lg transition-all duration-300 group overflow-hidden flex flex-col"
|
className="bg-white rounded-3xl shadow-sm border border-border hover:shadow-lg transition-all duration-300 group overflow-hidden flex flex-col md:flex-row"
|
||||||
data-testid="feature-card-3"
|
data-testid="feature-card-3"
|
||||||
>
|
>
|
||||||
<div className="h-52 overflow-hidden">
|
<div className="flex-1 p-8 md:p-10 flex flex-col justify-center">
|
||||||
<img
|
|
||||||
src={dataSrc}
|
|
||||||
alt="Secure local data storage and backup system"
|
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
|
||||||
data-testid="img-data-control"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="p-8 flex flex-col flex-1">
|
|
||||||
<div className="w-14 h-14 bg-green-100 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-green-200 transition-colors">
|
<div className="w-14 h-14 bg-green-100 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-green-200 transition-colors">
|
||||||
<Shield className="w-7 h-7 text-[#158915]" />
|
<Shield className="w-7 h-7 text-[#158915]" />
|
||||||
</div>
|
</div>
|
||||||
@@ -116,9 +108,16 @@ export function Features() {
|
|||||||
All patient data lives on your local computers. Automatic local and remote backup — always secure, always yours.
|
All patient data lives on your local computers. Automatic local and remote backup — always secure, always yours.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
<div className="md:w-1/2 h-64 md:h-auto overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={dataSrc}
|
||||||
|
alt="Secure local data storage and backup system"
|
||||||
|
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||||
|
data-testid="img-data-control"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export function Footer() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-accent font-medium text-lg">Chat and Go.</p>
|
<p className="text-accent font-medium text-lg">Chat and Go.</p>
|
||||||
|
<p className="text-sm mt-4">535 Worcester Rd, Framingham, MA 01701</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-16 md:justify-end">
|
<div className="flex gap-16 md:justify-end">
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function Hero() {
|
|||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.6, delay: 0.2 }}
|
transition={{ duration: 0.6, delay: 0.2 }}
|
||||||
>
|
>
|
||||||
No CDT Code Search. No Paying for Third Parties.
|
No Paying for Third-Party Billing Service. No CDT Code Search.
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
|
|
||||||
<motion.p
|
<motion.p
|
||||||
@@ -65,52 +65,6 @@ export function Hero() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Mock UI preview */}
|
|
||||||
<motion.div
|
|
||||||
className="mt-20 max-w-5xl mx-auto rounded-2xl overflow-hidden shadow-2xl shadow-primary/10 border border-border bg-white"
|
|
||||||
initial={{ opacity: 0, y: 40 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.8, delay: 0.5 }}
|
|
||||||
>
|
|
||||||
<div className="h-12 bg-secondary flex items-center px-4 gap-2 border-b border-border">
|
|
||||||
<div className="w-3 h-3 rounded-full bg-red-400" />
|
|
||||||
<div className="w-3 h-3 rounded-full bg-amber-400" />
|
|
||||||
<div className="w-3 h-3 rounded-full bg-green-400" />
|
|
||||||
<div className="ml-4 h-6 w-64 bg-white rounded-md opacity-50 flex items-center px-2">
|
|
||||||
<div className="text-[10px] text-muted-foreground font-mono">my-dental-software.app</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-8 grid md:grid-cols-3 gap-6 bg-secondary/30">
|
|
||||||
<div className="md:col-span-2 space-y-4">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<div className="w-8 h-8 rounded-full bg-primary/10 flex-shrink-0" />
|
|
||||||
<div className="bg-white p-4 rounded-2xl rounded-tl-sm shadow-sm border border-border text-sm max-w-md">
|
|
||||||
Check if John Smith is eligible for a crown
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4 justify-end">
|
|
||||||
<div className="bg-primary text-white p-4 rounded-2xl rounded-tr-sm shadow-sm text-sm max-w-md">
|
|
||||||
<strong className="block mb-1 text-accent">Done.</strong>
|
|
||||||
John Smith is eligible for D2740 (Crown - porcelain/ceramic) through Delta Dental. Copay is 20%. Remaining annual maximum: $1,250.
|
|
||||||
</div>
|
|
||||||
<div className="w-8 h-8 rounded-full bg-primary flex items-center justify-center flex-shrink-0">
|
|
||||||
<div className="w-3 h-3 bg-accent rounded-sm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bg-primary rounded-xl shadow-sm p-5 h-full flex flex-col justify-center gap-4">
|
|
||||||
<p className="text-sm text-white">
|
|
||||||
We prefer John to come at 10am this Thursday because of a patient cancellation. Do you allow me to call John?
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
data-testid="button-call-john"
|
|
||||||
className="self-start px-4 py-2 rounded-full bg-white text-primary text-sm font-semibold hover:opacity-90 transition-opacity"
|
|
||||||
>
|
|
||||||
Call John
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,20 +64,20 @@ export function HowItWorks() {
|
|||||||
<div className="flex flex-col gap-6 h-[400px]">
|
<div className="flex flex-col gap-6 h-[400px]">
|
||||||
{/* Chat Mockup */}
|
{/* Chat Mockup */}
|
||||||
<div className="self-end bg-secondary p-4 rounded-2xl rounded-tr-none text-sm max-w-[80%]">
|
<div className="self-end bg-secondary p-4 rounded-2xl rounded-tr-none text-sm max-w-[80%]">
|
||||||
Can you pull up today's schedule and highlight any cancellations?
|
Check if John Smith is eligible for a crown
|
||||||
</div>
|
</div>
|
||||||
<div className="self-start bg-primary text-white p-4 rounded-2xl rounded-tl-none text-sm max-w-[80%] shadow-md relative">
|
<div className="self-start bg-primary text-white p-4 rounded-2xl rounded-tl-none text-sm max-w-[80%] shadow-md relative">
|
||||||
<div className="absolute -left-2 top-0 w-4 h-4 bg-primary transform rotate-45" />
|
<div className="absolute -left-2 top-0 w-4 h-4 bg-primary transform rotate-45" />
|
||||||
<span className="text-accent font-medium mb-1 block">Assistant</span>
|
<span className="text-accent font-medium mb-1 block">AI Assistant</span>
|
||||||
You have 12 appointments today. One cancellation at 2:00 PM (Jane Doe - Cleaning). I've already texted the waitlist.
|
Done. John Smith is eligible for D2740 (Crown - porcelain/ceramic) through Delta Dental. Copay is 20%. Remaining annual maximum: $1,250.
|
||||||
</div>
|
|
||||||
<div className="self-end bg-secondary p-4 rounded-2xl rounded-tr-none text-sm max-w-[80%] mt-4">
|
|
||||||
Great, file the claim for Michael's extraction yesterday.
|
|
||||||
</div>
|
</div>
|
||||||
<div className="self-start bg-primary text-white p-4 rounded-2xl rounded-tl-none text-sm max-w-[80%] shadow-md relative">
|
<div className="self-start bg-primary text-white p-4 rounded-2xl rounded-tl-none text-sm max-w-[80%] shadow-md relative">
|
||||||
<div className="absolute -left-2 top-0 w-4 h-4 bg-primary transform rotate-45" />
|
<div className="absolute -left-2 top-0 w-4 h-4 bg-primary transform rotate-45" />
|
||||||
<span className="text-accent font-medium mb-1 block">Assistant</span>
|
<span className="text-accent font-medium mb-1 block">AI Assistant</span>
|
||||||
Claim filed for Michael (D7140) to MetLife. Sent at 9:14 AM.
|
We prefer John to come at 10am this Thursday because of a patient cancellation. Do you allow me to call John?
|
||||||
|
<button className="mt-3 bg-white text-primary font-semibold text-xs px-4 py-2 rounded-full hover:bg-accent hover:text-primary transition-colors block">
|
||||||
|
Call John
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function Navbar() {
|
|||||||
className="rounded-full bg-primary text-primary-foreground hover:bg-primary/90 px-6 h-10"
|
className="rounded-full bg-primary text-primary-foreground hover:bg-primary/90 px-6 h-10"
|
||||||
data-testid="nav-get-started"
|
data-testid="nav-get-started"
|
||||||
>
|
>
|
||||||
Get Started Free
|
Sales: 508-882-2542
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -158,3 +158,6 @@ overrides:
|
|||||||
# drizzle-kit uses esbuild internally on an older version that's vulnerable, this overrides it
|
# drizzle-kit uses esbuild internally on an older version that's vulnerable, this overrides it
|
||||||
"@esbuild-kit/esm-loader": "npm:tsx@^4.21.0"
|
"@esbuild-kit/esm-loader": "npm:tsx@^4.21.0"
|
||||||
esbuild: "0.27.3"
|
esbuild: "0.27.3"
|
||||||
|
|
||||||
|
allowBuilds:
|
||||||
|
esbuild: true
|
||||||
|
|||||||
Reference in New Issue
Block a user