Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
231 lines (224 loc) · 16.6 KB
/
Copy pathindex.html
File metadata and controls
231 lines (224 loc) · 16.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HO15 — Deployed Full-Stack App · ForgeMind AI</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://unpkg.com/lucide@latest"></script>
<style>
:root{
--pageBg:#F7F7F3; --sidebarBg:#FAF9F5; --headerBg:#0F0F10; --headerText:#F5F5F2; --headerMuted:#A1A1AA;
--headerBorder:rgba(255,255,255,.12); --cardBg:#fff; --border:#E5E5E0;
--text:#111; --textSec:#6B7280; --textMuted:#999; --primary:#dc2626; --primaryHover:#b91c1c;
--hover:#EFEEE6; --green:#0F6E56; --purple:#534AB7; --amber:#854F0B;
--shadowSm:0 1px 2px rgba(0,0,0,.08); --shadowMd:0 8px 24px rgba(0,0,0,.06);
--font:'DM Sans',system-ui,sans-serif; --mono:'DM Mono',monospace;
}
*{box-sizing:border-box;}
html,body{margin:0;height:100%;}
body{font-family:var(--font);background:var(--pageBg);color:var(--text);}
a{color:inherit;text-decoration:none;}
/* dark header — full width */
header{height:56px;background:var(--headerBg);color:var(--headerText);display:flex;align-items:center;
flex-shrink:0;border-bottom:1px solid var(--headerBorder);}
.brand{width:220px;display:flex;align-items:center;gap:10px;padding-left:20px;height:100%;
border-right:1px solid var(--headerBorder);}
.wordmark{font-weight:900;font-size:16px;letter-spacing:.02em;display:flex;align-items:center;gap:2px;}
.wordmark .badge{background:var(--primary);color:#fff;padding:1px 6px;border-radius:5px;margin-left:2px;font-weight:800;}
.header-right{margin-left:auto;padding-right:20px;color:var(--headerMuted);font-size:12px;font-weight:500;display:flex;align-items:center;gap:8px;}
.header-right .dot{width:6px;height:6px;border-radius:99px;background:var(--green);}
/* shell = sidebar + main */
.shell{display:flex;height:calc(100vh - 56px);}
/* sidebar */
.sidebar{width:220px;flex-shrink:0;background:var(--sidebarBg);border-right:1px solid var(--border);
padding:16px 12px;display:flex;flex-direction:column;overflow-y:auto;}
.side-back{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:8px;font-size:13px;font-weight:500;
color:var(--textSec);margin-bottom:10px;}
.side-back:hover{background:var(--hover);color:var(--text);}
.side-back svg{width:16px;height:16px;}
.side-label{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.1em;color:var(--textMuted);
text-transform:uppercase;margin:8px 12px 6px;}
.nav a{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:8px;font-size:13px;font-weight:500;
color:var(--text);margin-bottom:2px;transition:background .12s;}
.nav a svg{width:17px;height:17px;opacity:.75;flex-shrink:0;}
.nav a:hover{background:var(--hover);}
.nav a.active{background:var(--primary);color:#fff;font-weight:700;box-shadow:0 2px 8px rgba(220,38,38,.25);}
.nav a.active svg{opacity:1;}
.side-foot{margin-top:auto;padding:14px 12px 4px;font-family:var(--mono);font-size:10px;color:var(--textMuted);
border-top:1px solid var(--border);}
/* main — full width */
main{flex:1;min-width:0;overflow-y:auto;padding:34px 44px 72px;}
.maxw{max-width:1180px;}
.eyebrow{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--primary);}
h1{font-size:32px;font-weight:700;margin:8px 0 14px;letter-spacing:-.01em;}
.pills{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:26px;}
.pill{font-size:11px;font-weight:600;padding:4px 11px;border-radius:99px;font-family:var(--mono);}
.pill.wk{background:#E7F0FE;color:#1877F2;} .pill.mod{background:#EEEDFE;color:#3C3489;}
.pill.fmt{background:#E1F5EE;color:#0F6E56;} .pill.kind{background:#FAEEDA;color:#854F0B;}
section{scroll-margin-top:20px;}
.done{display:flex;align-items:flex-start;gap:14px;background:var(--cardBg);border:1px solid var(--border);
border-radius:8px;box-shadow:var(--shadowSm);padding:20px 22px;margin-bottom:8px;}
.done .ic{width:38px;height:38px;border-radius:8px;background:#FCEBEB;color:var(--primary);display:flex;
align-items:center;justify-content:center;flex-shrink:0;}
.done .ic svg{width:20px;height:20px;}
.done .cap{font-size:9px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--textMuted);}
.done .val{font-size:16px;font-weight:600;margin-top:4px;line-height:1.45;}
.section-h{display:flex;align-items:center;gap:9px;font-size:20px;font-weight:600;margin:40px 0 14px;}
.section-h svg{width:20px;height:20px;color:var(--primary);}
.lead{font-size:14px;color:var(--textSec);line-height:1.75;max-width:900px;}
.lead b{color:var(--text);}
code{font-family:var(--mono);font-size:12px;background:#F1F0EA;padding:1px 5px;border-radius:4px;}
/* sample grid — fills the width */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:16px;margin-top:6px;}
.sample{background:var(--cardBg);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadowSm);
padding:18px 20px;display:flex;flex-direction:column;gap:10px;transition:box-shadow .15s,transform .15s,border-color .15s;}
.sample:hover{box-shadow:var(--shadowMd);transform:translateY(-1px);border-color:#d9d9d2;}
.sample .top{display:flex;align-items:center;gap:10px;}
.sample .num{font-family:var(--mono);font-size:11px;font-weight:500;color:#fff;background:var(--primary);
width:24px;height:24px;border-radius:99px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.sample .st{font-size:15px;font-weight:600;}
.sample p{font-size:13px;line-height:1.55;color:var(--textSec);margin:0;flex:1;}
.sample .succ{font-size:12px;color:var(--green);font-weight:500;line-height:1.4;}
.sample .open{display:inline-flex;align-items:center;gap:7px;align-self:flex-start;margin-top:2px;
background:#FCEBEB;color:var(--primary);font-size:12px;font-weight:600;font-family:var(--mono);
padding:7px 13px;border-radius:7px;transition:background .12s;}
.sample .open:hover{background:var(--primary);color:#fff;}
.sample .open svg{width:14px;height:14px;}
/* steps */
.steps{counter-reset:s;display:flex;flex-direction:column;gap:12px;background:var(--cardBg);border:1px solid var(--border);
border-radius:8px;box-shadow:var(--shadowSm);padding:20px 22px;max-width:900px;}
.step{display:flex;gap:13px;align-items:flex-start;}
.step .n{counter-increment:s;font-family:var(--mono);font-size:12px;font-weight:500;color:var(--primary);
border:1.5px solid var(--border);border-radius:99px;width:27px;height:27px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.step .n::before{content:counter(s);}
.step p{font-size:13.5px;line-height:1.55;margin:3px 0;}
/* tip */
.tip{background:#FDF6F6;border:1px solid #F3C6C6;border-radius:8px;padding:16px 20px;margin-top:8px;max-width:900px;}
.tip+.tip{margin-top:16px;}
.tip .th{display:flex;align-items:center;gap:8px;font-weight:600;font-size:14px;color:var(--primary);margin-bottom:6px;}
.tip .th svg{width:16px;height:16px;}
.tip ul{margin:6px 0 0;padding-left:18px;font-size:13px;color:var(--textSec);line-height:1.65;}
@media(max-width:820px){.sidebar{display:none;} main{padding:26px 22px 60px;} h1{font-size:26px;}}
</style>
</head>
<body>
<header>
<div class="brand"><span class="wordmark">FORGE<span class="badge">COURSE</span></span></div>
<span class="header-right"><span class="dot"></span>ForgeMind AI · Hands-On Portfolio</span>
</header>
<div class="shell">
<aside class="sidebar">
<a class="side-back" href="https://course.srv879786.hstgr.cloud/"><i data-lucide="grid-2x2"></i> All Hands-On</a>
<div class="side-label">On this page</div>
<nav class="nav">
<a href="#overview" class="active"><i data-lucide="target"></i> Overview</a>
<a href="#build"><i data-lucide="cpu"></i> What you build</a>
<a href="#samples"><i data-lucide="list-checks"></i> Problem statements</a>
<a href="#howto"><i data-lucide="git-branch"></i> How to use</a>
<a href="#tips"><i data-lucide="battery-charging"></i> Pro-plan tips</a>
</nav>
<div class="side-foot">ForgeMind AI<br>AI Productivity Essentials<br>HO15 · Module M17 · Week 6</div>
</aside>
<main>
<div class="maxw">
<section id="overview">
<div class="eyebrow">Hands-On 15 · Week 6 · Module M17 — Deployed Full-Stack App · Google Workspace</div>
<h1>Deployed Full-Stack App · Google Workspace</h1>
<div class="pills">
<span class="pill wk">Week 6</span>
<span class="pill mod">M17 · Deploy</span>
<span class="pill fmt">Solo</span>
<span class="pill kind">Deployed</span>
</div>
<div class="done">
<div class="ic"><i data-lucide="target"></i></div>
<div>
<div class="cap">Done when</div>
<div class="val">A deployed full-stack app wired into Google Workspace — Sheets, Gmail or Calendar as its live data and actions.</div>
</div>
</div>
</section>
<section id="build">
<div class="section-h"><i data-lucide="server"></i> What you build</div>
<p class="lead">A real <b>full-stack app</b> — a frontend, a backend and an <b>OAuth connection into Google Workspace</b> — that you run on <b>localhost</b> first and then <b>deploy live</b> to a public URL. Instead of a throwaway database, your app reads and writes <b>Google Sheets, Gmail or Calendar</b> as its live data and actions: a Sheet becomes the database, Gmail becomes the outbox, Calendar becomes the schedule. Ship the full source (frontend + backend) plus a README naming the live URL and which Google product it connects to and what it reads and writes.</p>
</section>
<section id="samples">
<div class="section-h"><i data-lucide="list-checks"></i> Pick a problem statement</div>
<p class="lead" style="margin-bottom:16px;">Each sample is a real, hosted build wired into a Google Workspace product — a Sheet, Gmail or Calendar as its live data and actions. Open one live, or bring your own idea (get it approved first).</p>
<div class="grid">
<div class="sample">
<div class="top"><span class="num">1</span><span class="st">Gmail Bulk Mailer</span></div>
<p>Your team sends the same personalised email to 200 people from a spreadsheet, one at a time. Build and deploy a Gmail bulk-mailer that reads a Google Sheet of contacts, merges each row into a template (name, company, custom fields) and sends through Gmail — with a live send queue and open/reply tracking.</p>
<span class="succ">✓ connect a sheet, preview the merged emails, and send a personalised batch from one dashboard — no more copy-paste</span>
<a class="open" href="https://course.srv879786.hstgr.cloud/samples/ho15-fullstack-app/sample-01/" target="_blank"><i data-lucide="external-link"></i> Open live sample</a>
</div>
<div class="sample">
<div class="top"><span class="num">2</span><span class="st">Sheet-Connected Live Dashboard</span></div>
<p>Your KPIs live in a Google Sheet nobody opens. Build and deploy a dashboard that uses a Google Sheet as its live database — summary cards, trend charts and tables that refresh when the sheet changes.</p>
<span class="succ">✓ point it at a sheet and the numbers and charts render live, with edits in the sheet showing up in the dashboard</span>
<a class="open" href="https://course.srv879786.hstgr.cloud/samples/ho15-fullstack-app/sample-02/" target="_blank"><i data-lucide="external-link"></i> Open live sample</a>
</div>
<div class="sample">
<div class="top"><span class="num">3</span><span class="st">Smart Intake Form → Sheet</span></div>
<p>Google Forms can't do conditional logic, real validation or a branded UI. Build and deploy a smart intake form — conditional fields, validation, file uploads, your branding — that writes every submission straight into a Google Sheet, with a filterable responses view.</p>
<span class="succ">✓ submit the form and watch the row land in the connected sheet and the responses table</span>
<a class="open" href="https://course.srv879786.hstgr.cloud/samples/ho15-fullstack-app/sample-03/" target="_blank"><i data-lucide="external-link"></i> Open live sample</a>
</div>
<div class="sample">
<div class="top"><span class="num">4</span><span class="st">Custom Google Calendar UI</span></div>
<p>Google Calendar's UI is one-size-fits-all. Build and deploy a personalised calendar/scheduling app on top of Google Calendar — your own themed month/week views, filters by project or client, availability slots and one-click booking.</p>
<span class="succ">✓ view and create events in a custom UI that stays in sync with your Google Calendar</span>
<a class="open" href="https://course.srv879786.hstgr.cloud/samples/ho15-fullstack-app/sample-04/" target="_blank"><i data-lucide="external-link"></i> Open live sample</a>
</div>
<div class="sample">
<div class="top"><span class="num">5</span><span class="st">Slides / Report Generator</span></div>
<p>You rebuild the same client report deck by hand every week. Build and deploy a generator that pulls numbers from a Google Sheet and produces a branded Google Slides deck (or PDF report) at one click.</p>
<span class="succ">✓ pick a data range and generate a ready-to-share deck with your charts and branding</span>
<a class="open" href="https://course.srv879786.hstgr.cloud/samples/ho15-fullstack-app/sample-05/" target="_blank"><i data-lucide="external-link"></i> Open live sample</a>
</div>
</div>
</section>
<section id="howto">
<div class="section-h"><i data-lucide="git-branch"></i> How to use this repo</div>
<div class="steps">
<div class="step"><span class="n"></span><p>Click <b>Use this template</b> to create your own copy.</p></div>
<div class="step"><span class="n"></span><p>Open a live sample above, then run it on <b>localhost</b> — e.g. <code>npm install</code> then <code>npm run dev</code> (backend + frontend, with a database). Rebuild it with the tested prompt in the README.</p></div>
<div class="step"><span class="n"></span><p>Deploy it to a public URL, then replace this README with what you built: the live URL and the data it stores.</p></div>
</div>
</section>
<section id="tips">
<div class="section-h"><i data-lucide="battery-charging"></i> Use your Claude.ai Pro plan wisely</div>
<div class="tip">
<div class="th"><i data-lucide="server"></i> Runs on your machine, then deploys</div>
<ul>
<li>This is a <b>full-stack app</b> (frontend + backend + database) you run on <b>localhost</b> first — this GitHub Pages page is static and can't run the backend, so it's just the landing page and README.</li>
<li>Follow the README to run it locally — e.g. <code>npm install</code> then <code>npm run dev</code> (backend + frontend, with a database).</li>
<li>Getting it to a live public URL is covered once in the course — see the <b>Week-6 deploy walkthrough</b> video.</li>
</ul>
</div>
<div class="tip">
<div class="th"><i data-lucide="badge-check"></i> Subscription-first — no API key</div>
<ul>
<li>Everything is built with your normal Claude.ai login (Pro or Team) — no API key needed.</li>
<li>Use the example prompt in each sample's README — it's already written and tested.</li>
<li>One clear prompt beats lots of vague back-and-forth — say what you want, with an example, in a single message.</li>
<li>Start a new chat when you switch tasks — long chats re-read every earlier message and use your limit faster.</li>
</ul>
</div>
</section>
</div>
</main>
</div>
<script>
lucide.createIcons();
// scroll-spy: highlight the sidebar link for the section in view
const links = [...document.querySelectorAll('.nav a')];
const map = {}; links.forEach(a=>{ const id=a.getAttribute('href').slice(1); if(id) map[id]=a; });
const obs = new IntersectionObserver((entries)=>{
entries.forEach(e=>{ if(e.isIntersecting){ links.forEach(l=>l.classList.remove('active')); map[e.target.id]?.classList.add('active'); }});
},{rootMargin:'-15% 0px -70% 0px',threshold:0});
document.querySelectorAll('main section[id]').forEach(s=>obs.observe(s));
</script>
</body>
</html>