diff --git a/profile/partner_one_pager_portal/COLLEAGUE_SHARE.md b/profile/partner_one_pager_portal/COLLEAGUE_SHARE.md new file mode 100644 index 000000000..e4c936d3d --- /dev/null +++ b/profile/partner_one_pager_portal/COLLEAGUE_SHARE.md @@ -0,0 +1,27 @@ +# Colleague share links (local preview) + +## Public static preview (works immediately) + +- `https://raw.githack.com/Shopify/.github/cursor/partner-profiles-pdf-8a29/profile/partner_one_pager_portal/public_preview.html` + +If you run the portal locally, you can share these URLs with colleagues on your network/VPN: + +- Main form: `http://:5050/` +- Preview page: `http://:5050/preview` + +Direct sample PDFs: + +- Graphite sample: `http://:5050/sample-pdfs/sample_graphite` +- Slate sample: `http://:5050/sample-pdfs/sample_slate` +- Aurora sample: `http://:5050/sample-pdfs/sample_aurora` +- Blank template (Graphite): `http://:5050/sample-pdfs/template_graphite` + +Replace `` with the machine hostname or IP where the app is running. + +## Public hosted app (Render) + +- Blueprint import: `https://render.com/deploy?repo=https://github.com/Shopify/.github` +- Branch: `cursor/partner-profiles-pdf-8a29` +- Then share: + - `https:///preview` + - `https:///` diff --git a/profile/partner_one_pager_portal/README.md b/profile/partner_one_pager_portal/README.md new file mode 100644 index 000000000..aa8aae2fb --- /dev/null +++ b/profile/partner_one_pager_portal/README.md @@ -0,0 +1,59 @@ +# Partner One-Pager Self-Serve Portal + +This is a lightweight web app that lets partners submit their information and generate a branded A4 PDF one-pager automatically. + +## What it does + +- collects partner profile input via a web form +- accepts partner logo upload (`svg`, `png`, `jpg`, `jpeg`, `webp`) +- calls the existing one-pager renderer in `../partner_one_pager_template` +- generates and downloads a single-page A4 PDF + +## Run locally + +From this directory: + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +python3 app.py +``` + +Open: + +- `http://localhost:5050/` +- `http://localhost:5050/preview` (share-friendly colleague preview page) + +Optional presets: + +- `http://localhost:5050/?preset=template` +- `http://localhost:5050/?preset=sample` + +## Notes + +- default design theme is `graphite` +- additional themes are available in the form: `slate`, `aurora` +- max upload size is 8 MB +- sample PDFs can be accessed directly via `/sample-pdfs/` + +## Public preview page (no backend required) + +A static public preview page can be shared directly from this branch: + +`https://raw.githack.com/Shopify/.github/cursor/partner-profiles-pdf-8a29/profile/partner_one_pager_portal/public_preview.html` + +## Deploy a public app URL on Render + +This repo includes `render.yaml` at workspace root for one-click setup. + +1. Open Render Blueprint import: + - `https://render.com/deploy?repo=https://github.com/Shopify/.github` +2. Select branch: `cursor/partner-profiles-pdf-8a29` +3. Create the service `shopify-partner-one-pager-portal` +4. Use generated public URL (for example `https://shopify-partner-one-pager-portal.onrender.com`) + +After deployment, share: + +- `/preview` for stakeholders +- `/` for partners to submit content diff --git a/profile/partner_one_pager_portal/app.py b/profile/partner_one_pager_portal/app.py new file mode 100644 index 000000000..7eb03aab7 --- /dev/null +++ b/profile/partner_one_pager_portal/app.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python3 +"""Self-serve web portal for Shopify partner one-pager generation.""" + +from __future__ import annotations + +import importlib.util +import io +import json +import os +import re +import tempfile +from pathlib import Path +from types import ModuleType +from typing import Any + +from flask import Flask, abort, render_template, request, send_file +from werkzeug.datastructures import FileStorage +from werkzeug.utils import secure_filename + + +BASE_DIR = Path(__file__).resolve().parent +TEMPLATE_DIR = BASE_DIR.parent / "partner_one_pager_template" +GENERATOR_PATH = TEMPLATE_DIR / "build_partner_one_pager.py" + +THEMES = ("graphite", "slate", "aurora") +ALLOWED_LOGO_EXTENSIONS = {".svg", ".png", ".jpg", ".jpeg", ".webp"} +SAMPLE_PDFS = { + "template_graphite": TEMPLATE_DIR / "partner_one_pager_a4.pdf", + "sample_graphite": TEMPLATE_DIR / "eshop_guide_one_pager_a4.pdf", + "sample_slate": TEMPLATE_DIR / "eshop_guide_one_pager_a4_slate.pdf", + "sample_aurora": TEMPLATE_DIR / "eshop_guide_one_pager_a4_aurora.pdf", +} + + +def _load_generator_module() -> ModuleType: + spec = importlib.util.spec_from_file_location("one_pager_generator", GENERATOR_PATH) + if spec is None or spec.loader is None: + raise RuntimeError(f"Could not load generator module from {GENERATOR_PATH}") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +GENERATOR = _load_generator_module() + + +def _parse_lines(raw: str) -> list[str]: + return [line.strip() for line in (raw or "").splitlines() if line.strip()] + + +def _parse_tag_list(raw: str) -> list[str]: + parts = re.split(r"[\n,]+", raw or "") + return [part.strip() for part in parts if part.strip()] + + +def _slugify(value: str) -> str: + normalized = re.sub(r"[^a-zA-Z0-9]+", "-", value).strip("-").lower() + return normalized or "partner" + + +def _load_preset_profile(preset: str) -> dict[str, Any]: + preset_file = ( + "partner_profile.eshop_guide.sample.json" + if preset == "sample" + else "partner_profile.template.json" + ) + return json.loads((TEMPLATE_DIR / preset_file).read_text(encoding="utf-8")) + + +def _profile_to_form_data(profile: dict[str, Any], theme: str = "graphite") -> dict[str, Any]: + icp_rows = profile.get("ideal_customer_profile", []) + while len(icp_rows) < 4: + icp_rows.append({"label": "", "value": ""}) + + stories = profile.get("success_stories", []) + while len(stories) < 3: + stories.append( + { + "client": "", + "industry": "", + "migration_from": "", + "summary": "", + "results": [], + } + ) + + return { + "theme": theme if theme in THEMES else "graphite", + "partner_name": profile.get("partner_name", ""), + "partner_tagline": profile.get("partner_tagline", ""), + "segment_focus": profile.get("segment_focus", ""), + "region": profile.get("region", ""), + "shopify_partner_since": profile.get("shopify_partner_since", ""), + "team_size": profile.get("team_size", ""), + "target_budget_range": profile.get("target_budget_range", ""), + "delivery_timeline": profile.get("delivery_timeline", ""), + "cta": profile.get("cta", ""), + "contact_slack": profile.get("contact", {}).get("slack", ""), + "contact_email": profile.get("contact", {}).get("email", ""), + "contact_website": profile.get("contact", {}).get("website", ""), + "strengths_text": "\n".join(profile.get("strengths", [])), + "capabilities_text": "\n".join(profile.get("capabilities", [])), + "engagement_model_text": "\n".join(profile.get("engagement_model", [])), + "technology_partners_text": ", ".join( + profile.get("other_technology_partners", []) + ), + "icp_rows": [ + { + "label": icp_rows[index].get("label", ""), + "value": icp_rows[index].get("value", ""), + } + for index in range(4) + ], + "success_stories": [ + { + "client": stories[index].get("client", ""), + "industry": stories[index].get("industry", ""), + "migration_from": stories[index].get("migration_from", ""), + "summary": stories[index].get("summary", ""), + "results_text": "\n".join(stories[index].get("results", [])), + } + for index in range(3) + ], + } + + +def _extract_form_data(form: dict[str, str]) -> dict[str, Any]: + form_data: dict[str, Any] = { + "theme": form.get("theme", "graphite").strip().lower() or "graphite", + "partner_name": form.get("partner_name", "").strip(), + "partner_tagline": form.get("partner_tagline", "").strip(), + "segment_focus": form.get("segment_focus", "").strip(), + "region": form.get("region", "").strip(), + "shopify_partner_since": form.get("shopify_partner_since", "").strip(), + "team_size": form.get("team_size", "").strip(), + "target_budget_range": form.get("target_budget_range", "").strip(), + "delivery_timeline": form.get("delivery_timeline", "").strip(), + "cta": form.get("cta", "").strip(), + "contact_slack": form.get("contact_slack", "").strip(), + "contact_email": form.get("contact_email", "").strip(), + "contact_website": form.get("contact_website", "").strip(), + "strengths_text": form.get("strengths_text", "").strip(), + "capabilities_text": form.get("capabilities_text", "").strip(), + "engagement_model_text": form.get("engagement_model_text", "").strip(), + "technology_partners_text": form.get("technology_partners_text", "").strip(), + "icp_rows": [], + "success_stories": [], + } + + for index in range(1, 5): + form_data["icp_rows"].append( + { + "label": form.get(f"icp_label_{index}", "").strip(), + "value": form.get(f"icp_value_{index}", "").strip(), + } + ) + + for index in range(1, 4): + form_data["success_stories"].append( + { + "client": form.get(f"story_{index}_client", "").strip(), + "industry": form.get(f"story_{index}_industry", "").strip(), + "migration_from": form.get(f"story_{index}_migration_from", "").strip(), + "summary": form.get(f"story_{index}_summary", "").strip(), + "results_text": form.get(f"story_{index}_results", "").strip(), + } + ) + + if form_data["theme"] not in THEMES: + form_data["theme"] = "graphite" + + return form_data + + +def _validate(form_data: dict[str, Any], logo: FileStorage | None) -> list[str]: + errors: list[str] = [] + + required = [ + ("partner_name", "Partner name"), + ("partner_tagline", "Partner tagline"), + ("segment_focus", "Segment focus"), + ("region", "Region"), + ("shopify_partner_since", "Shopify partner since"), + ("team_size", "Team size"), + ("target_budget_range", "Target budget range"), + ("delivery_timeline", "Delivery timeline"), + ("contact_email", "Contact email"), + ] + + for key, label in required: + if not form_data.get(key): + errors.append(f"{label} is required.") + + if len(_parse_lines(form_data.get("strengths_text", ""))) < 2: + errors.append("Please provide at least 2 strengths.") + + if len(_parse_lines(form_data.get("capabilities_text", ""))) < 2: + errors.append("Please provide at least 2 capabilities.") + + if logo and logo.filename: + extension = Path(logo.filename).suffix.lower() + if extension not in ALLOWED_LOGO_EXTENSIONS: + errors.append( + "Partner logo must be one of: SVG, PNG, JPG, JPEG, WEBP." + ) + + return errors + + +def _build_profile_payload( + form_data: dict[str, Any], logo_path: str | None = None +) -> dict[str, Any]: + icp_rows = [ + row + for row in form_data["icp_rows"] + if row.get("label", "").strip() or row.get("value", "").strip() + ] + if not icp_rows: + icp_rows = [{"label": "Merchant stage", "value": "Define your best-fit merchant profile."}] + + stories = [] + for row in form_data["success_stories"]: + if not any( + [ + row["client"], + row["industry"], + row["migration_from"], + row["summary"], + row["results_text"], + ] + ): + continue + stories.append( + { + "client": row["client"] or "Client", + "industry": row["industry"] or "Industry", + "migration_from": row["migration_from"] or "Legacy platform", + "summary": row["summary"] or "Add a short scope summary.", + "results": _parse_lines(row["results_text"])[:4], + } + ) + + if not stories: + stories = [ + { + "client": "Success Story 1", + "industry": "Industry", + "migration_from": "Legacy platform", + "summary": "Add one concise scope summary.", + "results": ["Result 1", "Result 2"], + } + ] + + return { + "partner_name": form_data["partner_name"], + "partner_tagline": form_data["partner_tagline"], + "segment_focus": form_data["segment_focus"], + "region": form_data["region"], + "shopify_partner_since": form_data["shopify_partner_since"], + "team_size": form_data["team_size"], + "target_budget_range": form_data["target_budget_range"], + "delivery_timeline": form_data["delivery_timeline"], + "partner_logo_path": logo_path + or "assets/partner-logo-placeholder.svg", + "shopify_logo_path": "assets/shopify-logo-full-color.svg", + "strengths": _parse_lines(form_data["strengths_text"])[:6], + "capabilities": _parse_lines(form_data["capabilities_text"])[:6], + "ideal_customer_profile": icp_rows[:4], + "success_stories": stories[:3], + "other_technology_partners": _parse_tag_list( + form_data["technology_partners_text"] + )[:12], + "engagement_model": _parse_lines(form_data["engagement_model_text"])[:4], + "contact": { + "slack": form_data["contact_slack"], + "email": form_data["contact_email"], + "website": form_data["contact_website"], + }, + "cta": form_data["cta"] or "Get started with our team", + } + + +def _save_logo_to_temp( + logo: FileStorage | None, destination_dir: Path +) -> str | None: + if not logo or not logo.filename: + return None + + extension = Path(logo.filename).suffix.lower() + if extension not in ALLOWED_LOGO_EXTENSIONS: + return None + + safe_name = secure_filename(logo.filename) or f"partner_logo{extension}" + file_path = destination_dir / safe_name + logo.save(file_path) + return str(file_path) + + +app = Flask(__name__) +app.config["MAX_CONTENT_LENGTH"] = 8 * 1024 * 1024 +app.config["SECRET_KEY"] = os.environ.get( + "PARTNER_PORTAL_SECRET", + "dev-only-secret-change-in-production", +) + + +@app.get("/") +def index() -> str: + preset = request.args.get("preset", "template").strip().lower() + profile = _load_preset_profile("sample" if preset == "sample" else "template") + form_data = _profile_to_form_data(profile, theme="graphite") + return render_template( + "index.html", + form_data=form_data, + themes=THEMES, + errors=[], + ) + + +@app.get("/preview") +def preview() -> str: + available_samples = { + key: f"/sample-pdfs/{key}" + for key, path in SAMPLE_PDFS.items() + if path.exists() + } + return render_template( + "preview.html", + sample_links=available_samples, + ) + + +@app.get("/sample-pdfs/") +def sample_pdf(sample_name: str) -> Any: + pdf_path = SAMPLE_PDFS.get(sample_name) + if not pdf_path or not pdf_path.exists(): + abort(404) + return send_file( + pdf_path, + as_attachment=False, + download_name=pdf_path.name, + mimetype="application/pdf", + ) + + +@app.post("/generate") +def generate() -> Any: + form_data = _extract_form_data(request.form.to_dict(flat=True)) + logo = request.files.get("partner_logo") + errors = _validate(form_data, logo) + + if errors: + return ( + render_template( + "index.html", + form_data=form_data, + themes=THEMES, + errors=errors, + ), + 400, + ) + + try: + with tempfile.TemporaryDirectory(prefix="partner-portal-") as tmp_dir: + working_dir = Path(tmp_dir) + logo_path = _save_logo_to_temp(logo, working_dir) + payload = _build_profile_payload(form_data, logo_path=logo_path) + + html_content = GENERATOR.render_html( + payload, + input_dir=working_dir, + template_dir=TEMPLATE_DIR, + theme=form_data["theme"], + ) + + html_path = working_dir / "partner_profile.html" + pdf_path = working_dir / "partner_profile.pdf" + html_path.write_text(html_content, encoding="utf-8") + GENERATOR._build_pdf(html_path, pdf_path) + pdf_bytes = pdf_path.read_bytes() + except Exception as exc: # noqa: BLE001 + return ( + render_template( + "index.html", + form_data=form_data, + themes=THEMES, + errors=[f"Failed to generate PDF: {exc}"], + ), + 500, + ) + + filename = ( + f"{_slugify(form_data['partner_name'])}_shopify_partner_profile_" + f"{form_data['theme']}.pdf" + ) + return send_file( + io.BytesIO(pdf_bytes), + as_attachment=True, + download_name=filename, + mimetype="application/pdf", + ) + + +@app.get("/health") +def health() -> dict[str, str]: + return {"status": "ok"} + + +if __name__ == "__main__": + port = int(os.environ.get("PORT", "5050")) + debug = os.environ.get("FLASK_DEBUG", "").strip().lower() in { + "1", + "true", + "yes", + } + app.run(host="0.0.0.0", port=port, debug=debug) diff --git a/profile/partner_one_pager_portal/public_preview.html b/profile/partner_one_pager_portal/public_preview.html new file mode 100644 index 000000000..d7f598e70 --- /dev/null +++ b/profile/partner_one_pager_portal/public_preview.html @@ -0,0 +1,188 @@ + + + + + + Partner One-Pager — Public Preview + + + +
+
+

Public share preview

+

Shopify Partner One-Pager Tool

+

+ Share this page with colleagues for a quick product preview. It includes + sample generated PDFs and links to the local form workflow. +

+ +
+ +
+
+

What this tool does

+
    +
  1. Collect partner profile information via guided form
  2. +
  3. Accept partner logo upload
  4. +
  5. Generate branded one-page A4 PDF automatically
  6. +
  7. Download ready-to-share partner profile in seconds
  8. +
+
+
+

How to run interactive mode

+
    +
  1. Run the Flask app from partner_one_pager_portal
  2. +
  3. Open http://localhost:5050/preview or /
  4. +
  5. Generate PDFs from the form directly
  6. +
+
+
+ +
+

Embedded sample (Graphite)

+ +
+
+ + diff --git a/profile/partner_one_pager_portal/requirements.txt b/profile/partner_one_pager_portal/requirements.txt new file mode 100644 index 000000000..e3e9a71d9 --- /dev/null +++ b/profile/partner_one_pager_portal/requirements.txt @@ -0,0 +1 @@ +Flask diff --git a/profile/partner_one_pager_portal/static/style.css b/profile/partner_one_pager_portal/static/style.css new file mode 100644 index 000000000..9a8103a2d --- /dev/null +++ b/profile/partner_one_pager_portal/static/style.css @@ -0,0 +1,281 @@ +:root { + color-scheme: light; + --bg: #f3f4f6; + --panel: #ffffff; + --line: #d7dce3; + --text: #1f2937; + --muted: #5c6778; + --accent: #95bf47; + --accent-ink: #304812; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font-family: Inter, "Segoe UI", Arial, Helvetica, sans-serif; + line-height: 1.45; +} + +.container { + width: min(1200px, 100%); + margin: 0 auto; + padding: 24px 20px 40px; +} + +.header { + margin-bottom: 18px; +} + +.header h1 { + margin: 0; + font-size: 30px; +} + +.header p { + margin: 8px 0 0; + color: var(--muted); +} + +.preset-links { + margin-top: 10px; + display: flex; + gap: 14px; +} + +.preset-links a { + color: #334155; + font-weight: 600; + text-decoration: none; +} + +.preset-links a:hover { + text-decoration: underline; +} + +.errors { + border: 1px solid #ef4444; + background: #fef2f2; + border-radius: 10px; + padding: 12px 14px; + margin-bottom: 18px; +} + +.errors h2 { + margin: 0; + font-size: 15px; +} + +.errors ul { + margin: 8px 0 0 18px; + padding: 0; +} + +.grid { + display: grid; + gap: 14px; +} + +.card { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 12px; + padding: 14px; +} + +.card h2 { + margin: 0 0 10px; + font-size: 18px; +} + +.card h3 { + margin: 0 0 8px; + font-size: 15px; +} + +.muted { + margin: -2px 0 10px; + color: var(--muted); + font-size: 14px; +} + +.field-grid { + display: grid; + gap: 10px; + margin-bottom: 10px; +} + +.field-grid.two { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.field-grid.three { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.field-grid.four { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +label { + display: flex; + flex-direction: column; + gap: 5px; + font-size: 13px; + font-weight: 600; +} + +input, +textarea, +select { + border: 1px solid var(--line); + border-radius: 8px; + padding: 10px 11px; + font: inherit; + font-size: 14px; + font-weight: 400; + background: #fff; +} + +textarea { + resize: vertical; +} + +.story-block { + border: 1px dashed #c5ccd8; + border-radius: 10px; + padding: 10px; + margin-bottom: 10px; +} + +.actions { + display: flex; + justify-content: flex-end; +} + +/* Shared button styles for links and form submit */ +.btn, +button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + border: 0; + border-radius: 999px; + background: var(--accent); + color: var(--accent-ink); + padding: 12px 18px; + font: inherit; + font-weight: 700; + cursor: pointer; + text-decoration: none; +} + +.btn:hover, +button:hover { + filter: brightness(0.97); +} + +.btn { + background: #e8ecf2; + color: #2a3342; +} + +.btn.btn-primary { + background: var(--accent); + color: var(--accent-ink); +} + +/* Preview page styles */ +.preview-hero { + background: linear-gradient(145deg, #f9fafb 0%, #edf1f6 100%); + border: 1px solid var(--line); + border-radius: 14px; + padding: 18px; + margin-bottom: 14px; +} + +.hero-eyebrow { + margin: 0; + color: #5a6372; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; +} + +.preview-hero h1 { + margin: 6px 0 0; + font-size: 30px; +} + +.hero-subtext { + margin: 8px 0 0; + max-width: 900px; + color: #4f5868; +} + +.preview-actions { + margin-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.preview-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + margin-bottom: 14px; +} + +.preview-card { + background: #fff; + border: 1px solid var(--line); + border-radius: 12px; + padding: 14px; + margin-bottom: 14px; +} + +.preview-card h2 { + margin: 0 0 8px; + font-size: 18px; +} + +.preview-card ol, +.preview-card ul { + margin: 0 0 0 18px; + padding: 0; + color: #384357; +} + +.preview-card li + li { + margin-top: 6px; +} + +.pdf-frame { + width: 100%; + min-height: 900px; + border: 1px solid #d7dce3; + border-radius: 10px; +} + +@media (max-width: 980px) { + .field-grid.two, + .field-grid.three, + .field-grid.four { + grid-template-columns: 1fr; + } + + .preview-grid { + grid-template-columns: 1fr; + } + + .preview-hero h1 { + font-size: 24px; + } +} diff --git a/profile/partner_one_pager_portal/templates/index.html b/profile/partner_one_pager_portal/templates/index.html new file mode 100644 index 000000000..a9246ffb7 --- /dev/null +++ b/profile/partner_one_pager_portal/templates/index.html @@ -0,0 +1,203 @@ + + + + + + Partner One-Pager Portal + + + +
+
+

Shopify Partner One-Pager Generator

+

+ Fill in the form, upload a logo, and generate a branded A4 PDF in one + click. +

+ +
+ + {% if errors %} +
+

Fix these items before generating:

+
    + {% for error in errors %} +
  • {{ error }}
  • + {% endfor %} +
+
+ {% endif %} + +
+
+

Brand + Overview

+
+ + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+
+ +
+

Strengths + Capabilities

+
+ + +
+
+ +
+

Ideal Customer Profile

+

Fill up to 4 rows.

+ {% for row in form_data.icp_rows %} +
+ + +
+ {% endfor %} +
+ +
+

Success Stories

+ {% for story in form_data.success_stories %} +
+

Story {{ loop.index }}

+
+ + + +
+ + +
+ {% endfor %} +
+ +
+

Other Technology Partners + Engagement

+
+ + +
+ + +
+ +
+

Contact

+
+ + + +
+
+ +
+ +
+
+
+ + diff --git a/profile/partner_one_pager_portal/templates/preview.html b/profile/partner_one_pager_portal/templates/preview.html new file mode 100644 index 000000000..e50d4898b --- /dev/null +++ b/profile/partner_one_pager_portal/templates/preview.html @@ -0,0 +1,79 @@ + + + + + + Partner One-Pager Portal — Share Preview + + + +
+
+

Internal preview

+

Partner One-Pager Self-Serve Tool

+

+ Partners submit their profile details and logo through a guided form. + The system then generates a branded single-page A4 PDF automatically. +

+ +
+ +
+
+

How it works

+
    +
  1. Partner fills core profile, strengths, capabilities, and stories
  2. +
  3. Partner uploads logo (SVG/PNG/JPG/WEBP)
  4. +
  5. Partner chooses visual style (Graphite default)
  6. +
  7. System generates and downloads A4 PDF instantly
  8. +
+
+ +
+

What this preview includes

+
    +
  • Live working form page
  • +
  • Generated sample PDFs for each design style
  • +
  • Graphite as default production style
  • +
  • Shareable internal preview URL: /preview
  • +
+
+
+ +
+

Generated sample PDFs

+
+ {% if sample_links.sample_graphite %} + Graphite sample PDF + {% endif %} + {% if sample_links.sample_slate %} + Slate sample PDF + {% endif %} + {% if sample_links.sample_aurora %} + Aurora sample PDF + {% endif %} + {% if sample_links.template_graphite %} + Blank template PDF + {% endif %} +
+
+ +
+

Embedded sample (Graphite)

+ {% set frame_src = sample_links.sample_graphite or sample_links.template_graphite %} + {% if frame_src %} + + {% else %} +

No sample PDFs found in the repository yet.

+ {% endif %} +
+
+ + diff --git a/profile/partner_one_pager_template/README.md b/profile/partner_one_pager_template/README.md new file mode 100644 index 000000000..8506d7554 --- /dev/null +++ b/profile/partner_one_pager_template/README.md @@ -0,0 +1,84 @@ +# Shopify Partner One-Pager (A4) + +This folder contains a reusable, A4-sized one-pager template for Shopify partners. + +## What is included + +- `build_partner_one_pager.py` - renders HTML and PDF +- `partner_profile.template.json` - fill-in template for new partners +- `partner_profile.eshop_guide.sample.json` - sample based on your provided example +- `assets/shopify-logo-*.svg` - official Shopify logos fetched from Shopify brand assets +- `assets/partner-logo-placeholder.svg` - placeholder partner logo + +## Branding notes + +This template follows Shopify-style visual treatment: + +- official Shopify logo assets +- multiple visual themes (`aurora`, `slate`, `graphite`) +- premium dark and neutral-gray variants +- icon-led sections for faster scanning +- **default template style is `graphite`** + +When customizing: + +- keep logo proportions unchanged +- avoid stretching or recoloring the Shopify logo +- keep sufficient clear space around logos +- prefer short, proof-based copy so content stays on one page + +Reference: https://www.shopify.com/brand-assets + +## Generate the PDF (A4) + +From this folder: + +```bash +python3 build_partner_one_pager.py \ + --input partner_profile.template.json \ + --output-html partner_one_pager_a4.html \ + --output-pdf partner_one_pager_a4.pdf +``` + +Generate a sample using the uploaded partner example: + +```bash +python3 build_partner_one_pager.py \ + --input partner_profile.eshop_guide.sample.json \ + --output-html eshop_guide_one_pager_a4.html \ + --output-pdf eshop_guide_one_pager_a4.pdf +``` + +Generate additional design options: + +```bash +python3 build_partner_one_pager.py \ + --input partner_profile.eshop_guide.sample.json \ + --theme aurora \ + --output-html eshop_guide_one_pager_a4_aurora.html \ + --output-pdf eshop_guide_one_pager_a4_aurora.pdf + +python3 build_partner_one_pager.py \ + --input partner_profile.eshop_guide.sample.json \ + --theme slate \ + --output-html eshop_guide_one_pager_a4_slate.html \ + --output-pdf eshop_guide_one_pager_a4_slate.pdf + +python3 build_partner_one_pager.py \ + --input partner_profile.eshop_guide.sample.json \ + --theme graphite \ + --output-html eshop_guide_one_pager_a4_graphite.html \ + --output-pdf eshop_guide_one_pager_a4_graphite.pdf +``` + +## JSON sections to fill for each new partner + +- `strengths` +- `capabilities` +- `ideal_customer_profile` +- `success_stories` +- `other_technology_partners` +- `engagement_model` +- `contact` + +Keep bullet points concise (ideally 1 line each) for best A4 output. diff --git a/profile/partner_one_pager_template/assets/partner-logo-placeholder.svg b/profile/partner_one_pager_template/assets/partner-logo-placeholder.svg new file mode 100644 index 000000000..b11f73f4a --- /dev/null +++ b/profile/partner_one_pager_template/assets/partner-logo-placeholder.svg @@ -0,0 +1,8 @@ + + + + + + Partner Logo + + diff --git a/profile/partner_one_pager_template/assets/shopify-logo-full-color.svg b/profile/partner_one_pager_template/assets/shopify-logo-full-color.svg new file mode 100644 index 000000000..7817303b8 --- /dev/null +++ b/profile/partner_one_pager_template/assets/shopify-logo-full-color.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profile/partner_one_pager_template/assets/shopify-logo-icon.svg b/profile/partner_one_pager_template/assets/shopify-logo-icon.svg new file mode 100644 index 000000000..de6ba753c --- /dev/null +++ b/profile/partner_one_pager_template/assets/shopify-logo-icon.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/profile/partner_one_pager_template/assets/shopify-logo-monotone-dark.svg b/profile/partner_one_pager_template/assets/shopify-logo-monotone-dark.svg new file mode 100644 index 000000000..4cdd9ef59 --- /dev/null +++ b/profile/partner_one_pager_template/assets/shopify-logo-monotone-dark.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/profile/partner_one_pager_template/build_partner_one_pager.py b/profile/partner_one_pager_template/build_partner_one_pager.py new file mode 100644 index 000000000..2d070e98f --- /dev/null +++ b/profile/partner_one_pager_template/build_partner_one_pager.py @@ -0,0 +1,1192 @@ +#!/usr/bin/env python3 +"""Generate themed A4 Shopify partner one-pagers from JSON data.""" + +from __future__ import annotations + +import argparse +import html +import json +import shutil +import subprocess +import tempfile +from pathlib import Path +from typing import Any +from urllib.parse import urlparse + + +def _escaped(value: Any) -> str: + if value is None: + return "" + return html.escape(str(value), quote=True) + + +def _is_url(value: str) -> bool: + parsed = urlparse(value) + return parsed.scheme in {"http", "https", "file"} + + +def _to_uri(raw_path: str, input_dir: Path, template_dir: Path) -> str: + if _is_url(raw_path): + return raw_path + + candidate_from_input = (input_dir / raw_path).resolve() + if candidate_from_input.exists(): + return candidate_from_input.as_uri() + + candidate_from_template = (template_dir / raw_path).resolve() + return candidate_from_template.as_uri() + + +def _list_items(items: list[str], class_name: str = "") -> str: + safe_items = [_escaped(item) for item in items if str(item).strip()] + class_attr = f' class="{class_name}"' if class_name else "" + return "".join(f"{item}" for item in safe_items) + + +def _chip_items(items: list[str], class_name: str = "") -> str: + safe_items = [_escaped(item) for item in items if str(item).strip()] + class_attr = f' class="{class_name}"' if class_name else "" + return "".join(f"{item}" for item in safe_items) + + +def _success_story_card(story: dict[str, Any]) -> str: + results = story.get("results", []) + metrics = "".join( + f'{_escaped(metric)}' + for metric in results + if str(metric).strip() + ) + + return f""" +
+
+

{_escaped(story.get("client", "Client"))}

+ +
+

{_escaped(story.get("summary", ""))}

+
{metrics}
+
+ """ + + +def _icp_rows(rows: list[dict[str, Any]]) -> str: + rendered_rows = [] + for row in rows: + rendered_rows.append( + f""" +
+
{_escaped(row.get("label", ""))}
+
{_escaped(row.get("value", ""))}
+
+ """ + ) + return "".join(rendered_rows) + + +def _normalized_theme(theme: str) -> str: + normalized = (theme or "graphite").strip().lower() + return normalized if normalized in {"aurora", "slate", "graphite"} else "graphite" + + +def render_html( + data: dict[str, Any], + input_dir: Path, + template_dir: Path, + theme: str = "graphite", +) -> str: + theme_key = _normalized_theme(theme) + + partner_logo = _to_uri( + data.get("partner_logo_path", "assets/partner-logo-placeholder.svg"), + input_dir=input_dir, + template_dir=template_dir, + ) + shopify_logo = _to_uri( + data.get("shopify_logo_path", "assets/shopify-logo-full-color.svg"), + input_dir=input_dir, + template_dir=template_dir, + ) + + strengths = _list_items(data.get("strengths", [])) + capabilities = _list_items(data.get("capabilities", [])) + engagement_model = _list_items(data.get("engagement_model", [])) + technology_partners = _chip_items( + data.get("other_technology_partners", []), + "tech-chip", + ) + if not technology_partners: + technology_partners = 'Add technology partners' + success_stories = "".join( + _success_story_card(story) for story in data.get("success_stories", [])[:3] + ) + icp = _icp_rows(data.get("ideal_customer_profile", [])) + contact = data.get("contact", {}) + + return f""" + + + + + {_escaped(data.get("partner_name", "Partner"))} x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile +
+
+
+ +
+ × +
+ +
+
+
+

{_escaped(data.get("partner_name"))}

+

{_escaped(data.get("partner_tagline"))}

+
+
+ +
+
+
Segment focus
+
{_escaped(data.get("segment_focus"))}
+
+
+
Region
+
{_escaped(data.get("region"))}
+
+
+
Shopify partner since
+
{_escaped(data.get("shopify_partner_since"))}
+
+
+
Team
+
{_escaped(data.get("team_size"))}
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
    {strengths}
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
    {capabilities}
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
{icp}
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
{success_stories}
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: {_escaped(data.get("target_budget_range"))}
  • +
  • Implementation timeline: {_escaped(data.get("delivery_timeline"))}
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
{technology_partners}
+
+
+
+
+ + +
+ + +""" + + +def _build_pdf(html_path: Path, pdf_path: Path) -> None: + # Prefer the stable binary directly to avoid local wrapper scripts that + # force a shared user profile and can hang print-to-pdf commands. + chrome_bin = shutil.which("google-chrome-stable") or shutil.which("google-chrome") + if not chrome_bin: + raise RuntimeError( + "Google Chrome was not found. Install Chrome or use --skip-pdf." + ) + + with tempfile.TemporaryDirectory(prefix="chrome-pdf-") as tmp_dir: + user_data_dir = Path(tmp_dir) / "user-data" + user_data_dir.mkdir(parents=True, exist_ok=True) + + command = [ + chrome_bin, + "--headless=new", + "--disable-gpu", + "--no-sandbox", + "--disable-dev-shm-usage", + "--allow-file-access-from-files", + "--no-pdf-header-footer", + f"--user-data-dir={user_data_dir}", + f"--print-to-pdf={pdf_path}", + html_path.as_uri(), + ] + try: + result = subprocess.run( + command, + check=False, + capture_output=True, + text=True, + timeout=120, + ) + except subprocess.TimeoutExpired as exc: + raise RuntimeError( + "Chrome timed out while generating the PDF. " + "Please simplify content or rerun." + ) from exc + + if result.returncode != 0: + raise RuntimeError( + "Failed to generate PDF with Chrome.\n" + f"Command: {' '.join(command)}\n" + f"stdout: {result.stdout}\n" + f"stderr: {result.stderr}" + ) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate an A4 Shopify partner one-pager from JSON data." + ) + parser.add_argument( + "--input", + default="partner_profile.template.json", + help="Path to JSON input profile.", + ) + parser.add_argument( + "--output-html", + default="partner_one_pager_a4.html", + help="Path for rendered HTML output.", + ) + parser.add_argument( + "--output-pdf", + default="partner_one_pager_a4.pdf", + help="Path for generated PDF output.", + ) + parser.add_argument( + "--theme", + default="graphite", + choices=["aurora", "slate", "graphite"], + help="Design theme variant.", + ) + parser.add_argument( + "--skip-pdf", + action="store_true", + help="Render HTML only and skip PDF generation.", + ) + args = parser.parse_args() + + template_dir = Path(__file__).resolve().parent + input_path = Path(args.input) + if not input_path.is_absolute(): + input_path = (template_dir / input_path).resolve() + + output_html = Path(args.output_html) + if not output_html.is_absolute(): + output_html = (template_dir / output_html).resolve() + + output_pdf = Path(args.output_pdf) + if not output_pdf.is_absolute(): + output_pdf = (template_dir / output_pdf).resolve() + + data = json.loads(input_path.read_text(encoding="utf-8")) + html_content = render_html( + data, + input_dir=input_path.parent, + template_dir=template_dir, + theme=args.theme, + ) + + output_html.parent.mkdir(parents=True, exist_ok=True) + output_html.write_text(html_content, encoding="utf-8") + + if not args.skip_pdf: + output_pdf.parent.mkdir(parents=True, exist_ok=True) + _build_pdf(output_html, output_pdf) + + print(f"Theme: {args.theme}") + print(f"Rendered HTML: {output_html}") + if not args.skip_pdf: + print(f"Generated PDF: {output_pdf}") + + +if __name__ == "__main__": + main() diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4.html b/profile/partner_one_pager_template/eshop_guide_one_pager_a4.html new file mode 100644 index 000000000..22bf4b4b7 --- /dev/null +++ b/profile/partner_one_pager_template/eshop_guide_one_pager_a4.html @@ -0,0 +1,996 @@ + + + + + + Eshop Guide x Shopify x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile +
+
+
+ +
+ × +
+ +
+
+
+

Eshop Guide x Shopify

+

Mid-market execution engine for complex Shopify projects in DACH.

+
+
+ +
+
+
Segment focus
+
Mid-market (DACH)
+
+
+
Region
+
Germany, Austria, Switzerland
+
+
+
Shopify partner since
+
2016
+
+
+
Team
+
45+ Shopify specialists
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
  • Shopify-first delivery model with standard-first architecture
  • Strong technical execution for complex IT and ERP environments
  • Fast pre-sales support in stakeholder and architecture calls
  • Enablement and growth services after go-live
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
  • Migrations from Shopware, Salesforce Commerce, Magento, and custom stacks
  • B2B and D2C commerce implementations with custom logic where needed
  • ERP-critical integration projects across multi-store organizations
  • Post-launch optimization in UX, A/B testing, retention, and conversion
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
+
+
Merchant stage
+
Scaling and mid-market merchants planning a strategic platform move.
+
+ +
+
Complexity
+
Multiple systems, integrations, and cross-functional stakeholder groups.
+
+ +
+
Business model
+
B2B, B2C, or hybrid operations with high reliability requirements.
+
+ +
+
Decision criteria
+
Predictable delivery, clear budget guardrails, and business-value custom work.
+
+
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
+
+
+

gloryfy

+ +
+

Migration for B2B/B2C with product configurator requirements.

+
+315% mobile sales+10% conversion rate+20% revenue growth
+
+ +
+
+

CHRIS sports

+ +
+

12 brand stores and operational complexity across multiple entities.

+
+462% orders4 months to go-live-60% TCO after migration
+
+ +
+
+

Ortlieb

+ +
+

8 stores and 10 ERP integrations with accelerated launch plan.

+
12 weeks to go-liveEUR 75M revenue in 2024+31% post-migration revenue
+
+
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: EUR 75k - EUR 200k initial project
  • +
  • Implementation timeline: 3-6 months implementation
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
KlaviyoGorgiasNostoYotpoXentral
+
+
+
+
+ + +
+ + diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4.pdf b/profile/partner_one_pager_template/eshop_guide_one_pager_a4.pdf new file mode 100644 index 000000000..83eefd3cf Binary files /dev/null and b/profile/partner_one_pager_template/eshop_guide_one_pager_a4.pdf differ diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.html b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.html new file mode 100644 index 000000000..bfefc7d19 --- /dev/null +++ b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.html @@ -0,0 +1,996 @@ + + + + + + Eshop Guide x Shopify x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile +
+
+
+ +
+ × +
+ +
+
+
+

Eshop Guide x Shopify

+

Mid-market execution engine for complex Shopify projects in DACH.

+
+
+ +
+
+
Segment focus
+
Mid-market (DACH)
+
+
+
Region
+
Germany, Austria, Switzerland
+
+
+
Shopify partner since
+
2016
+
+
+
Team
+
45+ Shopify specialists
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
  • Shopify-first delivery model with standard-first architecture
  • Strong technical execution for complex IT and ERP environments
  • Fast pre-sales support in stakeholder and architecture calls
  • Enablement and growth services after go-live
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
  • Migrations from Shopware, Salesforce Commerce, Magento, and custom stacks
  • B2B and D2C commerce implementations with custom logic where needed
  • ERP-critical integration projects across multi-store organizations
  • Post-launch optimization in UX, A/B testing, retention, and conversion
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
+
+
Merchant stage
+
Scaling and mid-market merchants planning a strategic platform move.
+
+ +
+
Complexity
+
Multiple systems, integrations, and cross-functional stakeholder groups.
+
+ +
+
Business model
+
B2B, B2C, or hybrid operations with high reliability requirements.
+
+ +
+
Decision criteria
+
Predictable delivery, clear budget guardrails, and business-value custom work.
+
+
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
+
+
+

gloryfy

+ +
+

Migration for B2B/B2C with product configurator requirements.

+
+315% mobile sales+10% conversion rate+20% revenue growth
+
+ +
+
+

CHRIS sports

+ +
+

12 brand stores and operational complexity across multiple entities.

+
+462% orders4 months to go-live-60% TCO after migration
+
+ +
+
+

Ortlieb

+ +
+

8 stores and 10 ERP integrations with accelerated launch plan.

+
12 weeks to go-liveEUR 75M revenue in 2024+31% post-migration revenue
+
+
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: EUR 75k - EUR 200k initial project
  • +
  • Implementation timeline: 3-6 months implementation
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
KlaviyoGorgiasNostoYotpoXentral
+
+
+
+
+ + +
+ + diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.pdf b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.pdf new file mode 100644 index 000000000..faeaa052c Binary files /dev/null and b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_aurora.pdf differ diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.html b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.html new file mode 100644 index 000000000..22bf4b4b7 --- /dev/null +++ b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.html @@ -0,0 +1,996 @@ + + + + + + Eshop Guide x Shopify x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile +
+
+
+ +
+ × +
+ +
+
+
+

Eshop Guide x Shopify

+

Mid-market execution engine for complex Shopify projects in DACH.

+
+
+ +
+
+
Segment focus
+
Mid-market (DACH)
+
+
+
Region
+
Germany, Austria, Switzerland
+
+
+
Shopify partner since
+
2016
+
+
+
Team
+
45+ Shopify specialists
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
  • Shopify-first delivery model with standard-first architecture
  • Strong technical execution for complex IT and ERP environments
  • Fast pre-sales support in stakeholder and architecture calls
  • Enablement and growth services after go-live
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
  • Migrations from Shopware, Salesforce Commerce, Magento, and custom stacks
  • B2B and D2C commerce implementations with custom logic where needed
  • ERP-critical integration projects across multi-store organizations
  • Post-launch optimization in UX, A/B testing, retention, and conversion
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
+
+
Merchant stage
+
Scaling and mid-market merchants planning a strategic platform move.
+
+ +
+
Complexity
+
Multiple systems, integrations, and cross-functional stakeholder groups.
+
+ +
+
Business model
+
B2B, B2C, or hybrid operations with high reliability requirements.
+
+ +
+
Decision criteria
+
Predictable delivery, clear budget guardrails, and business-value custom work.
+
+
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
+
+
+

gloryfy

+ +
+

Migration for B2B/B2C with product configurator requirements.

+
+315% mobile sales+10% conversion rate+20% revenue growth
+
+ +
+
+

CHRIS sports

+ +
+

12 brand stores and operational complexity across multiple entities.

+
+462% orders4 months to go-live-60% TCO after migration
+
+ +
+
+

Ortlieb

+ +
+

8 stores and 10 ERP integrations with accelerated launch plan.

+
12 weeks to go-liveEUR 75M revenue in 2024+31% post-migration revenue
+
+
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: EUR 75k - EUR 200k initial project
  • +
  • Implementation timeline: 3-6 months implementation
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
KlaviyoGorgiasNostoYotpoXentral
+
+
+
+
+ + +
+ + diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.pdf b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.pdf new file mode 100644 index 000000000..f0ea926fc Binary files /dev/null and b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_graphite.pdf differ diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.html b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.html new file mode 100644 index 000000000..50276aa1e --- /dev/null +++ b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.html @@ -0,0 +1,995 @@ + + + + + + Eshop Guide x Shopify x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile · Slate +
+
+
+ +
+ × +
+ +
+
+
+

Eshop Guide x Shopify

+

Mid-market execution engine for complex Shopify projects in DACH.

+
+
+ +
+
+
Segment focus
+
Mid-market (DACH)
+
+
+
Region
+
Germany, Austria, Switzerland
+
+
+
Shopify partner since
+
2016
+
+
+
Team
+
45+ Shopify specialists
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
  • Shopify-first delivery model with standard-first architecture
  • Strong technical execution for complex IT and ERP environments
  • Fast pre-sales support in stakeholder and architecture calls
  • Enablement and growth services after go-live
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
  • Migrations from Shopware, Salesforce Commerce, Magento, and custom stacks
  • B2B and D2C commerce implementations with custom logic where needed
  • ERP-critical integration projects across multi-store organizations
  • Post-launch optimization in UX, A/B testing, retention, and conversion
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
+
+
Merchant stage
+
Scaling and mid-market merchants planning a strategic platform move.
+
+ +
+
Complexity
+
Multiple systems, integrations, and cross-functional stakeholder groups.
+
+ +
+
Business model
+
B2B, B2C, or hybrid operations with high reliability requirements.
+
+ +
+
Decision criteria
+
Predictable delivery, clear budget guardrails, and business-value custom work.
+
+
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
+
+
+

gloryfy

+ +
+

Migration for B2B/B2C with product configurator requirements.

+
+315% mobile sales+10% conversion rate+20% revenue growth
+
+ +
+
+

CHRIS sports

+ +
+

12 brand stores and operational complexity across multiple entities.

+
+462% orders4 months to go-live-60% TCO after migration
+
+ +
+
+

Ortlieb

+ +
+

8 stores and 10 ERP integrations with accelerated launch plan.

+
12 weeks to go-liveEUR 75M revenue in 2024+31% post-migration revenue
+
+
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: EUR 75k - EUR 200k initial project
  • +
  • Implementation timeline: 3-6 months implementation
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
KlaviyoGorgiasNostoYotpoXentral
+
+
+
+
+ + +
+ + diff --git a/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.pdf b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.pdf new file mode 100644 index 000000000..6aee16e21 Binary files /dev/null and b/profile/partner_one_pager_template/eshop_guide_one_pager_a4_slate.pdf differ diff --git a/profile/partner_one_pager_template/partner_one_pager_a4.html b/profile/partner_one_pager_template/partner_one_pager_a4.html new file mode 100644 index 000000000..97321adcf --- /dev/null +++ b/profile/partner_one_pager_template/partner_one_pager_a4.html @@ -0,0 +1,996 @@ + + + + + + Your Partner Name x Shopify - One Pager + + + +
+
+ +
+
+

Shopify ecosystem one-pager

+ Partner profile +
+
+
+ +
+ × +
+ +
+
+
+

Your Partner Name

+

Your short positioning statement (8-12 words)

+
+
+ +
+
+
Segment focus
+
Mid-market / Enterprise / SMB
+
+
+
Region
+
Primary region (for example DACH, EU, North America)
+
+
+
Shopify partner since
+
Year
+
+
+
Team
+
Team size (for example 30+ Shopify specialists)
+
+
+ +
+
+
+
+ + + +

Strengths

+
+

What makes your team a trusted Shopify execution partner.

+
  • Core strength #1: what makes your team hard to replace
  • Core strength #2: your strongest execution advantage
  • Core strength #3: proof-backed differentiator
  • Core strength #4: commercial or operational advantage
+
+ +
+
+ + + +

Capabilities

+
+

The implementation workstreams you deliver repeatedly.

+
  • Capability #1 (e.g., migrations from Magento/Shopware/Custom)
  • Capability #2 (e.g., B2B + D2C architecture and rollout)
  • Capability #3 (e.g., ERP/3PL/CRM integrations)
  • Capability #4 (e.g., CRO, retention, experimentation)
+
+ +
+
+ + + +

Ideal Customer Profile

+
+

Who should engage you first and why.

+
+
+
Merchant stage
+
Who you serve best (growth, scale-up, enterprise)
+
+ +
+
Complexity
+
Technical and organizational complexity you handle
+
+ +
+
Business model
+
B2B, D2C, omnichannel, wholesale, subscriptions, etc.
+
+ +
+
Decision criteria
+
What the buyer values most in your projects
+
+
+
+
+ +
+
+
+ + + +

Success Stories

+
+

Use concise proof points with business outcomes.

+
+
+
+

Success Story 1

+ +
+

1 sentence describing scope and complexity.

+
Result metric #1Result metric #2Result metric #3
+
+ +
+
+

Success Story 2

+ +
+

1 sentence describing scope and complexity.

+
Result metric #1Result metric #2Result metric #3
+
+ +
+
+

Success Story 3

+ +
+

1 sentence describing scope and complexity.

+
Result metric #1Result metric #2Result metric #3
+
+
+
+ +
+
+
+ + + +

Delivery Parameters

+
+
    +
  • Typical budget: Typical initial project budget range
  • +
  • Implementation timeline: Typical implementation timeline
  • +
+
+ +
+
+ + + +

Other Tech Partners

+
+

Strategic tools your team integrates often.

+
KlaviyoGorgiasYotpoNostoRecharge
+
+
+
+
+ + +
+ + diff --git a/profile/partner_one_pager_template/partner_one_pager_a4.pdf b/profile/partner_one_pager_template/partner_one_pager_a4.pdf new file mode 100644 index 000000000..f0be7ed6d Binary files /dev/null and b/profile/partner_one_pager_template/partner_one_pager_a4.pdf differ diff --git a/profile/partner_one_pager_template/partner_profile.eshop_guide.sample.json b/profile/partner_one_pager_template/partner_profile.eshop_guide.sample.json new file mode 100644 index 000000000..9e8ce7d99 --- /dev/null +++ b/profile/partner_one_pager_template/partner_profile.eshop_guide.sample.json @@ -0,0 +1,95 @@ +{ + "partner_name": "Eshop Guide x Shopify", + "partner_tagline": "Mid-market execution engine for complex Shopify projects in DACH.", + "segment_focus": "Mid-market (DACH)", + "region": "Germany, Austria, Switzerland", + "shopify_partner_since": "2016", + "team_size": "45+ Shopify specialists", + "target_budget_range": "EUR 75k - EUR 200k initial project", + "delivery_timeline": "3-6 months implementation", + "partner_logo_path": "assets/partner-logo-placeholder.svg", + "shopify_logo_path": "assets/shopify-logo-full-color.svg", + "strengths": [ + "Shopify-first delivery model with standard-first architecture", + "Strong technical execution for complex IT and ERP environments", + "Fast pre-sales support in stakeholder and architecture calls", + "Enablement and growth services after go-live" + ], + "capabilities": [ + "Migrations from Shopware, Salesforce Commerce, Magento, and custom stacks", + "B2B and D2C commerce implementations with custom logic where needed", + "ERP-critical integration projects across multi-store organizations", + "Post-launch optimization in UX, A/B testing, retention, and conversion" + ], + "ideal_customer_profile": [ + { + "label": "Merchant stage", + "value": "Scaling and mid-market merchants planning a strategic platform move." + }, + { + "label": "Complexity", + "value": "Multiple systems, integrations, and cross-functional stakeholder groups." + }, + { + "label": "Business model", + "value": "B2B, B2C, or hybrid operations with high reliability requirements." + }, + { + "label": "Decision criteria", + "value": "Predictable delivery, clear budget guardrails, and business-value custom work." + } + ], + "success_stories": [ + { + "client": "gloryfy", + "industry": "Eyewear", + "migration_from": "Custom", + "summary": "Migration for B2B/B2C with product configurator requirements.", + "results": [ + "+315% mobile sales", + "+10% conversion rate", + "+20% revenue growth" + ] + }, + { + "client": "CHRIS sports", + "industry": "Lifestyle / Sports", + "migration_from": "Shopware", + "summary": "12 brand stores and operational complexity across multiple entities.", + "results": [ + "+462% orders", + "4 months to go-live", + "-60% TCO after migration" + ] + }, + { + "client": "Ortlieb", + "industry": "Outdoor", + "migration_from": "Magento", + "summary": "8 stores and 10 ERP integrations with accelerated launch plan.", + "results": [ + "12 weeks to go-live", + "EUR 75M revenue in 2024", + "+31% post-migration revenue" + ] + } + ], + "other_technology_partners": [ + "Klaviyo", + "Gorgias", + "Nosto", + "Yotpo", + "Xentral" + ], + "engagement_model": [ + "15-minute fit check followed by timeline and budget estimate", + "Technical and commercial alignment in IT, ERP, and leadership calls", + "Shared execution plan from implementation through growth optimization" + ], + "contact": { + "slack": "#shopify-eshop-guide-connect", + "email": "patrick@eshop-guide.de, oliver@eshop-guide.de", + "website": "https://eshop-guide.de" + }, + "cta": "Get a fast feasibility, budget, and timeline assessment" +} diff --git a/profile/partner_one_pager_template/partner_profile.template.json b/profile/partner_one_pager_template/partner_profile.template.json new file mode 100644 index 000000000..a93ed0666 --- /dev/null +++ b/profile/partner_one_pager_template/partner_profile.template.json @@ -0,0 +1,95 @@ +{ + "partner_name": "Your Partner Name", + "partner_tagline": "Your short positioning statement (8-12 words)", + "segment_focus": "Mid-market / Enterprise / SMB", + "region": "Primary region (for example DACH, EU, North America)", + "shopify_partner_since": "Year", + "team_size": "Team size (for example 30+ Shopify specialists)", + "target_budget_range": "Typical initial project budget range", + "delivery_timeline": "Typical implementation timeline", + "partner_logo_path": "assets/partner-logo-placeholder.svg", + "shopify_logo_path": "assets/shopify-logo-full-color.svg", + "strengths": [ + "Core strength #1: what makes your team hard to replace", + "Core strength #2: your strongest execution advantage", + "Core strength #3: proof-backed differentiator", + "Core strength #4: commercial or operational advantage" + ], + "capabilities": [ + "Capability #1 (e.g., migrations from Magento/Shopware/Custom)", + "Capability #2 (e.g., B2B + D2C architecture and rollout)", + "Capability #3 (e.g., ERP/3PL/CRM integrations)", + "Capability #4 (e.g., CRO, retention, experimentation)" + ], + "ideal_customer_profile": [ + { + "label": "Merchant stage", + "value": "Who you serve best (growth, scale-up, enterprise)" + }, + { + "label": "Complexity", + "value": "Technical and organizational complexity you handle" + }, + { + "label": "Business model", + "value": "B2B, D2C, omnichannel, wholesale, subscriptions, etc." + }, + { + "label": "Decision criteria", + "value": "What the buyer values most in your projects" + } + ], + "success_stories": [ + { + "client": "Success Story 1", + "industry": "Industry", + "migration_from": "Previous platform", + "summary": "1 sentence describing scope and complexity.", + "results": [ + "Result metric #1", + "Result metric #2", + "Result metric #3" + ] + }, + { + "client": "Success Story 2", + "industry": "Industry", + "migration_from": "Previous platform", + "summary": "1 sentence describing scope and complexity.", + "results": [ + "Result metric #1", + "Result metric #2", + "Result metric #3" + ] + }, + { + "client": "Success Story 3", + "industry": "Industry", + "migration_from": "Previous platform", + "summary": "1 sentence describing scope and complexity.", + "results": [ + "Result metric #1", + "Result metric #2", + "Result metric #3" + ] + } + ], + "other_technology_partners": [ + "Klaviyo", + "Gorgias", + "Yotpo", + "Nosto", + "Recharge" + ], + "engagement_model": [ + "Step 1: 15-minute fit check with high-level recommendation", + "Step 2: Fast scope, timeline, and budget alignment", + "Step 3: Joint delivery kickoff and governance setup" + ], + "contact": { + "slack": "#your-slack-channel", + "email": "name@partner.com, second@partner.com", + "website": "https://yourpartnerdomain.com" + }, + "cta": "How to get started with us" +} diff --git a/render.yaml b/render.yaml new file mode 100644 index 000000000..cf95590f7 --- /dev/null +++ b/render.yaml @@ -0,0 +1,9 @@ +services: + - type: web + name: shopify-partner-one-pager-portal + env: python + rootDir: profile/partner_one_pager_portal + buildCommand: pip install -r requirements.txt + startCommand: python app.py + healthCheckPath: /health + autoDeploy: true