Testimonails and Introscript updates
This commit is contained in:
+3
-4
@@ -156,11 +156,10 @@ nginx/goodwalk.co.nz.svelte.conf.example
|
||||
```
|
||||
|
||||
Important:
|
||||
- The normal `deploy.ps1` flow does not deploy or reload the shared nginx stack.
|
||||
- Copy the updated nginx config to `/docker/nginx/conf.d/goodwalk.co.nz.conf` and reload nginx once.
|
||||
- The repo example now uses Docker's internal resolver so future app/mail container rebuilds will not leave nginx pinned to stale upstream IPs.
|
||||
- `deploy.ps1` now copies the repo nginx config to `/docker/nginx/conf.d/goodwalk.co.nz.conf` and reloads the shared nginx container as part of deployment.
|
||||
- The repo nginx config uses Docker's internal resolver so future app/mail container rebuilds will not leave nginx pinned to stale upstream IPs.
|
||||
|
||||
Then reload nginx:
|
||||
Manual nginx commands, if you ever need them:
|
||||
|
||||
```bash
|
||||
docker compose -p nginx -f /docker/nginx/docker-compose.yml exec nginx nginx -t
|
||||
|
||||
+20
-1
@@ -20,6 +20,10 @@ $SshConfigPath = Join-Path $LocalProjectPath 'ssh-config'
|
||||
$RemoteDeploymentPath = '/docker/goodwalk-svelte'
|
||||
$ComposeFileName = 'docker-compose.prod.yml'
|
||||
$DockerProjectName = 'goodwalk-svelte'
|
||||
$NginxConfigSource = 'nginx/goodwalk.co.nz.svelte.conf.example'
|
||||
$NginxConfigTarget = '/docker/nginx/conf.d/goodwalk.co.nz.conf'
|
||||
$NginxComposeFile = '/docker/nginx/docker-compose.yml'
|
||||
$NginxProjectName = 'nginx'
|
||||
|
||||
# Optional deployment settings.
|
||||
$VerifyUrl = 'https://www.goodwalk.co.nz/api/health'
|
||||
@@ -161,6 +165,10 @@ Assert-NotBlank -Name 'LocalProjectPath' -Value $LocalProjectPath
|
||||
Assert-NotBlank -Name 'RemoteDeploymentPath' -Value $RemoteDeploymentPath
|
||||
Assert-NotBlank -Name 'ComposeFileName' -Value $ComposeFileName
|
||||
Assert-NotBlank -Name 'DockerProjectName' -Value $DockerProjectName
|
||||
Assert-NotBlank -Name 'NginxConfigSource' -Value $NginxConfigSource
|
||||
Assert-NotBlank -Name 'NginxConfigTarget' -Value $NginxConfigTarget
|
||||
Assert-NotBlank -Name 'NginxComposeFile' -Value $NginxComposeFile
|
||||
Assert-NotBlank -Name 'NginxProjectName' -Value $NginxProjectName
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($Service)) {
|
||||
$Service = $Service.Trim()
|
||||
@@ -196,6 +204,8 @@ Write-Host "[deploy] Local project path: $LocalProjectPath"
|
||||
Write-Host "[deploy] Remote deployment path: $RemoteDeploymentPath"
|
||||
Write-Host "[deploy] Remote compose file: $ComposeFileName"
|
||||
Write-Host "[deploy] Docker project name: $DockerProjectName"
|
||||
Write-Host "[deploy] Shared nginx config: $NginxConfigTarget"
|
||||
Write-Host "[deploy] Shared nginx compose file: $NginxComposeFile"
|
||||
Write-Host "[deploy] SSH target: $sshTarget"
|
||||
Write-Host "[deploy] SSH config: $SshConfigPath"
|
||||
if (-not [string]::IsNullOrWhiteSpace($Service)) {
|
||||
@@ -212,6 +222,7 @@ Write-Host ' - Only the top-level Goodwalk compose project will be updated.'
|
||||
Write-Host ' - Legacy WordPress/onboarding compose files are not used.'
|
||||
Write-Host ' - Remote .env files are preserved because they are not uploaded.'
|
||||
Write-Host ' - No global Docker prune/stop/delete commands are used.'
|
||||
Write-Host ' - Shared nginx will be updated and reloaded with the Docker-DNS-based config.'
|
||||
|
||||
if (-not $Force) {
|
||||
$confirmation = Read-Host "Type DEPLOY to continue with the remote path '$RemoteDeploymentPath'"
|
||||
@@ -253,7 +264,15 @@ try {
|
||||
'--compose-file',
|
||||
$ComposeFileName,
|
||||
'--project-name',
|
||||
$DockerProjectName
|
||||
$DockerProjectName,
|
||||
'--nginx-source',
|
||||
$NginxConfigSource,
|
||||
'--nginx-target',
|
||||
$NginxConfigTarget,
|
||||
'--nginx-compose-file',
|
||||
$NginxComposeFile,
|
||||
'--nginx-project-name',
|
||||
$NginxProjectName
|
||||
) + $(if (-not [string]::IsNullOrWhiteSpace($Service)) { @('--service', $Service) } else { @() }))
|
||||
|
||||
Write-Host ''
|
||||
|
||||
+1
-1
@@ -380,7 +380,7 @@ def client_email(data: BookingSubmission) -> str:
|
||||
style="max-width:600px;width:100%;border-radius:16px;overflow:hidden;
|
||||
box-shadow:0 4px 24px rgba(0,0,0,0.08);">
|
||||
|
||||
{_logo_header(subtitle="Auckland’s favourite dog walking service")}
|
||||
{_logo_header(subtitle="Professional dog walking services")}
|
||||
|
||||
<!-- Body -->
|
||||
<tr>
|
||||
|
||||
Generated
+4
-4
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "^5.2.11",
|
||||
"@sveltejs/kit": "^2.17.1",
|
||||
"@sveltejs/kit": "^2.59.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/svelte": "^5.3.1",
|
||||
@@ -1271,9 +1271,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.58.0",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.58.0.tgz",
|
||||
"integrity": "sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==",
|
||||
"version": "2.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.59.0.tgz",
|
||||
"integrity": "sha512-WeJaGKvDf3uVQB4bnDHhM+BXCY34LC1v0HiPqnSpvNkjB54r8DAUP1rpk73s+5zprIirEKtUcVfgh6+fPODjzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "^5.2.11",
|
||||
"@sveltejs/kit": "^2.17.1",
|
||||
"@sveltejs/kit": "^2.59.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/svelte": "^5.3.1",
|
||||
|
||||
@@ -6,12 +6,19 @@ DEPLOY_PATH=""
|
||||
COMPOSE_FILE=""
|
||||
PROJECT_NAME=""
|
||||
SERVICE_NAME=""
|
||||
NGINX_SOURCE=""
|
||||
NGINX_TARGET=""
|
||||
NGINX_COMPOSE_FILE=""
|
||||
NGINX_PROJECT_NAME=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
deploy-remote.sh --archive <path> --deploy-path <path> --compose-file <name> --project-name <name>
|
||||
deploy-remote.sh --archive <path> --deploy-path <path> --compose-file <name> --project-name <name> [--service <name>]
|
||||
deploy-remote.sh --archive <path> --deploy-path <path> --compose-file <name> --project-name <name> \
|
||||
[--service <name>] [--nginx-source <path>] [--nginx-target <path>] \
|
||||
[--nginx-compose-file <path>] [--nginx-project-name <name>]
|
||||
|
||||
This script only updates the main Goodwalk compose project at the specified
|
||||
deployment path. It does not touch unrelated Docker projects or global Docker
|
||||
@@ -46,6 +53,22 @@ while [[ $# -gt 0 ]]; do
|
||||
SERVICE_NAME="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--nginx-source)
|
||||
NGINX_SOURCE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--nginx-target)
|
||||
NGINX_TARGET="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--nginx-compose-file)
|
||||
NGINX_COMPOSE_FILE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--nginx-project-name)
|
||||
NGINX_PROJECT_NAME="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
@@ -66,6 +89,22 @@ fi
|
||||
[[ "$DEPLOY_PATH" != "/" ]] || fail "Refusing to deploy to /"
|
||||
[[ -f "$ARCHIVE_PATH" ]] || fail "Archive not found: $ARCHIVE_PATH"
|
||||
|
||||
nginx_args=("$NGINX_SOURCE" "$NGINX_TARGET" "$NGINX_COMPOSE_FILE" "$NGINX_PROJECT_NAME")
|
||||
nginx_args_present=0
|
||||
for value in "${nginx_args[@]}"; do
|
||||
if [[ -n "$value" ]]; then
|
||||
nginx_args_present=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if (( nginx_args_present )); then
|
||||
[[ -n "$NGINX_SOURCE" ]] || fail "--nginx-source is required when nginx deployment is enabled"
|
||||
[[ -n "$NGINX_TARGET" ]] || fail "--nginx-target is required when nginx deployment is enabled"
|
||||
[[ -n "$NGINX_COMPOSE_FILE" ]] || fail "--nginx-compose-file is required when nginx deployment is enabled"
|
||||
[[ -n "$NGINX_PROJECT_NAME" ]] || fail "--nginx-project-name is required when nginx deployment is enabled"
|
||||
fi
|
||||
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker compose)
|
||||
elif command -v docker-compose >/dev/null 2>&1; then
|
||||
@@ -89,6 +128,12 @@ echo "[deploy-remote] Docker project: $PROJECT_NAME"
|
||||
if [[ -n "$SERVICE_NAME" ]]; then
|
||||
echo "[deploy-remote] Target service: $SERVICE_NAME"
|
||||
fi
|
||||
if (( nginx_args_present )); then
|
||||
echo "[deploy-remote] Nginx config source: $NGINX_SOURCE"
|
||||
echo "[deploy-remote] Nginx config target: $NGINX_TARGET"
|
||||
echo "[deploy-remote] Nginx compose file: $NGINX_COMPOSE_FILE"
|
||||
echo "[deploy-remote] Nginx project: $NGINX_PROJECT_NAME"
|
||||
fi
|
||||
echo "[deploy-remote] Staging archive in: $STAGING_DIR"
|
||||
|
||||
mkdir -p "$DEPLOY_PATH"
|
||||
@@ -171,4 +216,19 @@ if [[ -z "$SERVICE_NAME" || "$SERVICE_NAME" == "app" || "$SERVICE_NAME" == "db"
|
||||
"${COMPOSE_CMD[@]}" -p "$PROJECT_NAME" -f "$COMPOSE_FILE" exec -T app node scripts/sync-homepage-content.mjs
|
||||
fi
|
||||
|
||||
if (( nginx_args_present )); then
|
||||
[[ -f "$DEPLOY_PATH/$NGINX_SOURCE" ]] || fail "Nginx config missing from deployment payload: $DEPLOY_PATH/$NGINX_SOURCE"
|
||||
[[ -f "$NGINX_COMPOSE_FILE" ]] || fail "Nginx compose file was not found on the server: $NGINX_COMPOSE_FILE"
|
||||
|
||||
echo "[deploy-remote] Updating shared nginx config to avoid stale container IPs"
|
||||
mkdir -p "$(dirname "$NGINX_TARGET")"
|
||||
cp "$DEPLOY_PATH/$NGINX_SOURCE" "$NGINX_TARGET"
|
||||
|
||||
echo "[deploy-remote] Validating nginx configuration"
|
||||
"${COMPOSE_CMD[@]}" -p "$NGINX_PROJECT_NAME" -f "$NGINX_COMPOSE_FILE" exec -T nginx nginx -t
|
||||
|
||||
echo "[deploy-remote] Reloading shared nginx"
|
||||
"${COMPOSE_CMD[@]}" -p "$NGINX_PROJECT_NAME" -f "$NGINX_COMPOSE_FILE" exec -T nginx nginx -s reload
|
||||
fi
|
||||
|
||||
echo "[deploy-remote] Remote deployment finished"
|
||||
|
||||
@@ -10,9 +10,21 @@
|
||||
<div id="intro">
|
||||
<div class="intro-inner">
|
||||
<div class="intro-trust-badge">
|
||||
<div class="intro-trust-mark" aria-hidden="true">
|
||||
<Icon name="fab fa-google" />
|
||||
</div>
|
||||
<a
|
||||
class="intro-trust-mark intro-trust-mark-link"
|
||||
href={intro.reviewCta.href}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
aria-label="Read our Google reviews"
|
||||
>
|
||||
<img
|
||||
class="intro-google-logo"
|
||||
src="/images/google-g-logo.svg"
|
||||
alt=""
|
||||
width="28"
|
||||
height="29"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div class="intro-trust-copy">
|
||||
<p>{intro.text}</p>
|
||||
@@ -24,7 +36,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<a href={intro.reviewCta.href} target="_blank" rel="noopener">
|
||||
<a class="intro-trust-cta" href={intro.reviewCta.href} target="_blank" rel="noopener">
|
||||
{intro.reviewCta.label}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<div class="modal-divider"></div>
|
||||
|
||||
<p class="modal-sub">
|
||||
In the meantime, feel free to follow along on Instagram for daily walks and happy dogs.
|
||||
In the meantime, feel free to follow along on Instagram for daily walks and happy dogs!
|
||||
</p>
|
||||
|
||||
<button class="modal-btn" type="button" on:click={onClose}>
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
export let testimonials: TestimonialContent[];
|
||||
export let heading = 'Why people choose us!';
|
||||
export let blurb =
|
||||
'Real dogs, real routines, trusted by happy owners. Follow along on Instagram to see the Tiny Gang out on their daily adventures.';
|
||||
"Happy owners, even happier dogs. Our Auckland dog walking clients love what the Tiny Gang brings to their dog's routine — and you can see why. Follow along on Instagram for daily adventures, wagging tails and the odd zoomie";
|
||||
export let instagramHref = 'https://www.instagram.com/goodwalk.nz/';
|
||||
export let instagramLabel = '@goodwalk.nz';
|
||||
export let instagramLabel = 'goodwalk.nz';
|
||||
|
||||
type TestimonialSlide = TestimonialContent & { imageUrl: string };
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ export const homepageContent: HomePageContent = {
|
||||
title: 'Happy pets,',
|
||||
subtitle: 'happy humans',
|
||||
body:
|
||||
'Offering tailored pack walks for small and medium dogs, and one-on-one walks for large breeds. Our walkers give personalised attention to each dog, easing stress, anxiety and ensuring a quality experience. Our expertise in small-medium breeds ensures tailored care for their unique needs. Join our',
|
||||
emphasis: 'TINY GANG!',
|
||||
'Professional dog walking services in Auckland for small, medium and large breeds. Our experienced walkers provide tailored pack walks for smaller dogs and one-on-one walks for larger breeds - giving every dog the personalised attention they deserve. We specialise in understanding the unique needs of small-to-medium breeds, helping to ease stress and anxiety while keeping tails wagging. Ready to join our',
|
||||
emphasis: 'TINY GANG?',
|
||||
cta: { label: 'Book now', href: '#newlead', variant: 'green' },
|
||||
imageUrl: '/images/auckland-dog-walking-happy-dogs-happy-humans.webp',
|
||||
imageAlt: 'Woman cuddling a dog for Goodwalk Auckland dog walking services'
|
||||
|
||||
@@ -299,6 +299,10 @@
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.intro-google-logo {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#intro p {
|
||||
font-size: 17px;
|
||||
line-height: 1.45;
|
||||
@@ -312,7 +316,7 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#intro a {
|
||||
.intro-trust-cta {
|
||||
gap: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 15px;
|
||||
|
||||
@@ -67,6 +67,7 @@ section {
|
||||
|
||||
.intro-trust-mark {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 56px;
|
||||
@@ -75,9 +76,50 @@ section {
|
||||
background: #fff;
|
||||
color: #e00706;
|
||||
font-size: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 1px rgba(14, 27, 41, 0.08);
|
||||
}
|
||||
|
||||
.intro-trust-mark-link {
|
||||
text-decoration: none;
|
||||
isolation: isolate;
|
||||
transition:
|
||||
transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
box-shadow 0.22s ease;
|
||||
}
|
||||
|
||||
.intro-trust-mark-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -30% 35%;
|
||||
background: linear-gradient(120deg, transparent 0%, rgba(255, 255, 255, 0.2) 30%, rgba(255, 255, 255, 0.92) 50%, rgba(255, 255, 255, 0.2) 70%, transparent 100%);
|
||||
transform: translateX(-220%) rotate(18deg);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
animation: introGoogleShine 4.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.intro-trust-mark-link:hover,
|
||||
.intro-trust-mark-link:focus-visible {
|
||||
transform: translateY(-1px) scale(1.04);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(14, 27, 41, 0.08),
|
||||
0 10px 24px rgba(17, 20, 24, 0.12);
|
||||
}
|
||||
|
||||
.intro-trust-mark-link:focus-visible {
|
||||
outline: 2px solid rgba(10, 48, 78, 0.28);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.intro-google-logo {
|
||||
display: block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 28px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.intro-trust-copy {
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -107,7 +149,7 @@ section {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#intro a {
|
||||
.intro-trust-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -121,11 +163,37 @@ section {
|
||||
box-shadow: inset 0 0 0 1px rgba(14, 27, 41, 0.08);
|
||||
}
|
||||
|
||||
#intro a .icon {
|
||||
.intro-trust-cta .icon {
|
||||
color: #e00706;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
@keyframes introGoogleShine {
|
||||
0%,
|
||||
64%,
|
||||
100% {
|
||||
transform: translateX(-220%) rotate(18deg);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
72% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
84% {
|
||||
transform: translateX(220%) rotate(18deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.intro-trust-mark-link,
|
||||
.intro-trust-mark-link::after {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
#promise,
|
||||
#testimonials,
|
||||
#info {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 262">
|
||||
<path fill="#4285F4" d="M255.68 133.53c0-8.89-.8-17.43-2.29-25.63H130.8v48.48h70.06c-3.02 16.31-12.2 30.12-25.99 39.35v32.65h41.95c24.54-22.6 38.86-55.92 38.86-94.85Z"/>
|
||||
<path fill="#34A853" d="M130.8 261.1c35.1 0 64.53-11.63 86.04-31.55l-41.95-32.65c-11.63 7.79-26.53 12.38-44.09 12.38-33.88 0-62.58-22.88-72.83-53.62H14.6v33.67c21.39 42.42 65.29 71.77 116.2 71.77Z"/>
|
||||
<path fill="#FBBC05" d="M57.97 155.65c-2.61-7.79-4.09-16.11-4.09-24.65s1.48-16.86 4.09-24.65V72.68H14.6C5.28 91.24 0 110.62 0 131s5.28 39.76 14.6 58.32l43.37-33.67Z"/>
|
||||
<path fill="#EA4335" d="M130.8 52.72c19.08 0 36.23 6.57 49.72 19.48l37.29-37.29C195.28 13.35 165.87 0 130.8 0 79.89 0 35.99 29.35 14.6 72.68l43.37 33.67c10.25-30.74 38.95-53.63 72.83-53.63Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 809 B |
Reference in New Issue
Block a user