Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
924742a
feat: Enhance Ads page with improved metadata, styles, and video play…
mike-doctorina Dec 23, 2025
86efd3d
fix: Update VIDEO_ID for YouTube player to correct video link
mike-doctorina Dec 24, 2025
30a33e5
feat: Add internationalization support and confirmation dialog for ad…
mike-doctorina Dec 24, 2025
d060837
feat: Add loading screen and error handling for video player; enhance…
mike-doctorina Dec 24, 2025
d176526
feat: Enhance URL parameter handling for video and callback; add vali…
mike-doctorina Dec 25, 2025
bb1d159
feat: Implement advanced analytics tracking for video engagement and …
mike-doctorina Dec 25, 2025
8019882
feat: Implement retry mechanism for video autoplay to ensure playback…
mike-doctorina Dec 25, 2025
d083448
feat: Implement postMessage communication for video events and intera…
mike-doctorina Dec 31, 2025
aeea2a7
feat: Update time info layout and styles for improved readability and…
mike-doctorina Dec 31, 2025
6b57a02
feat: Add locale parameter handling for improved language support in …
mike-doctorina Dec 31, 2025
9f76a9d
feat: Refactor callback payload construction for enhanced video analy…
mike-doctorina Dec 31, 2025
82cfed3
feat: Integrate callback payload into video state change and completi…
mike-doctorina Dec 31, 2025
3cb1fcd
feat: Remove redundant video metrics from completion payload for clea…
mike-doctorina Jan 5, 2026
a01c0f8
feat: Comment out close button in video player for future reference
mike-doctorina Jan 6, 2026
b4467a0
feat: Comment out session ID check in video completion handler for fu…
mike-doctorina Jan 6, 2026
551a30e
Update main.ts
mike-doctorina Jan 6, 2026
17539a3
feat: Add TODOs for displaying ads from a video list and iframe closu…
mike-doctorina Jan 7, 2026
80b5945
feat: Add TODO for auto starting YouTube video
mike-doctorina Jan 7, 2026
570bb39
fix: Update Cache-Control headers for better caching strategy
mike-doctorina Jan 8, 2026
4c46ff4
feat: Update TODOs for displaying ads and iframe closure functionality
mike-doctorina Jan 8, 2026
7efea87
feat: Mark iframe closure as complete and implement auto-closing afte…
mike-doctorina Jan 8, 2026
27513e7
feat: Remove confirmation dialog and related logic for closing video …
mike-doctorina Jan 8, 2026
677c4f6
feat: Comment out tryCloseTab function to prevent tab closure during …
mike-doctorina Jan 8, 2026
5029d62
feat: Remove unused completion screen and related styles
mike-doctorina Jan 8, 2026
428c76f
feat: Update TODOs for auto-starting YouTube video and adjust playbac…
mike-doctorina Jan 8, 2026
7034c14
feat: Implement internationalization for payment success page and enh…
mike-doctorina Jan 8, 2026
55d8884
feat: Enhance URL validation by supporting wildcard domains for redir…
mike-doctorina Jan 9, 2026
e3a3b91
feat: Refactor translations and improve redirect URL validation logic
mike-doctorina Jan 9, 2026
0874885
feat(stripe-success): extract and log Stripe success parameters
mike-doctorina Jan 9, 2026
27c9b1a
feat: Refactor formatStripeSuccessLog to use array for improved strin…
mike-doctorina Jan 9, 2026
3bb5c96
feat(tests): add new test cases for URL parameter extraction in extra…
mike-doctorina Jan 9, 2026
9ae27f2
feat(audio-controls): implement unmute overlay and volume controls fo…
mike-doctorina Jan 9, 2026
0095ab3
feat(audio-controls): add iOS device detection and adjust volume cont…
mike-doctorina Jan 9, 2026
94d064c
feat: update page title and description for consistency across ads
mike-doctorina Jan 9, 2026
b4535b4
feat(stripe-success): add environment parameter to Stripe success han…
mike-doctorina Jan 9, 2026
4fd12df
feat(stripe-success): update checkoutId handling and refine parameter…
mike-doctorina Jan 9, 2026
fbfee00
feat(stripe-success): enhance parameter handling by adding 'type' and…
mike-doctorina Jan 9, 2026
2c6dd90
feat(audio-controls): prevent event bubbling in unmute button handler
mike-doctorina Jan 9, 2026
8ac943f
feat(audio-controls): enhance unmute overlay interaction by preventin…
mike-doctorina Jan 14, 2026
1f04cd1
feat(audio-controls): improve unmute button interaction by consolidat…
mike-doctorina Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,73 @@
"uriFormat": "%s",
"action": "openExternally"
} */
},
/* ───── Tests ───── */
{
"name": "Run All Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"test"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": [
"<node_internals>/**"
]
},
{
"name": "Run Tests (Once)",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"test:run"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": [
"<node_internals>/**"
]
},
{
"name": "Run Tests UI",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"test:ui"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": [
"<node_internals>/**"
]
},
{
"name": "Debug Current Test File",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"test",
"--",
"${file}"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": [
"<node_internals>/**"
]
}
],
"inputs": [
Expand Down
62 changes: 39 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ npm run dev:tools # Tools page
```

Or open in browser:

- `http://localhost:3000/pages/index/index.html`
- `http://localhost:3000/pages/tools/index.html`

Expand Down Expand Up @@ -98,30 +99,41 @@ pages/my-new-page/
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="My New Page - Doctorina">
<meta name="theme-color" content="#667eea">
<title>My New Page - Doctorina</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
</head>
<body>
<head>
<meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Doctorina</title>
<meta name="description" content="Doctorina" />
<meta name="theme-color" content="#25D366" />

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.ico" />

<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<meta name="apple-mobile-web-app-title" content="Doctorina" />
<link rel="apple-touch-icon" href="icons/web-app-manifest-192x192.png" />
</head>

<body>
<div id="app"></div>
<script type="module" src="/pages/my-new-page/main.ts"></script>
</body>
</body>
</html>
```

4. **main.ts** - page code:

```typescript
import { initPage } from '~/shared/utils/page-init';
import './style.css';
import { initPage } from "~/shared/utils/page-init";
import "./style.css";

initPage('My New Page - Doctorina');
initPage("My New Page - Doctorina");

const app = document.getElementById('app');
const app = document.getElementById("app");
if (app) {
app.innerHTML = `
<div class="container">
Expand All @@ -145,32 +157,36 @@ if (app) {
Ready-to-use utilities are available in `src/shared/utils/`:

```typescript
import { initPage } from '~/shared/utils/page-init';
import { copyToClipboard } from '~/shared/utils/clipboard';
import { formatDate, getRelativeTime } from '~/shared/utils/date';
import { isValidEmail, isValidUrl } from '~/shared/utils/validation';
import { initPage } from "~/shared/utils/page-init";
import { copyToClipboard } from "~/shared/utils/clipboard";
import { formatDate, getRelativeTime } from "~/shared/utils/date";
import { isValidEmail, isValidUrl } from "~/shared/utils/validation";

// Initialize page
initPage('Page Title');
initPage("Page Title");

// Clipboard operations
await copyToClipboard('Text to copy');
await copyToClipboard("Text to copy");

// Date formatting
const formatted = formatDate(new Date());
const relative = getRelativeTime(new Date());

// Validation
if (isValidEmail(email)) { /* ... */ }
if (isValidUrl(url)) { /* ... */ }
if (isValidEmail(email)) {
/* ... */
}
if (isValidUrl(url)) {
/* ... */
}
```

## 🎨 Shared Styles

Import shared CSS variables and utilities:

```typescript
import '~/shared/styles/common.css';
import "~/shared/styles/common.css";
```

Available CSS variables:
Expand Down
12 changes: 6 additions & 6 deletions firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,29 @@
],
"headers": [
{
"source": "**/*.@(js|css|png|jpg|jpeg|gif|svg|webp|woff|woff2|ttf|eot|ico)",
"source": "**/*.@(html|json)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
"value": "no-cache, no-store, must-revalidate"
}
]
},
{
"source": "**/*.@(html|json)",
"source": "**/*.@(css|png|jpg|jpeg|gif|svg|webp|woff|woff2|ttf|eot|ico)",
"headers": [
{
"key": "Cache-Control",
"value": "no-store"
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "**/*.@(wasm|mjs|js)",
"source": "**/*.@(js|mjs)",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache, max-age=0, must-revalidate"
"value": "public, max-age=31536000, immutable"
}
]
}
Expand Down
Loading
Loading