webcodelab commited on
Commit
513a946
Β·
verified Β·
1 Parent(s): 8be9313

Create api_docs.html

Browse files
Files changed (1) hide show
  1. app/templates/api_docs.html +397 -0
app/templates/api_docs.html ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block title %}API Documentation – Resume Ranker API{% endblock %}
3
+
4
+ {% block extra_head %}
5
+ {# No extra head elements are needed here. Your base.html or dashboard.css should handle the main CSS. #}
6
+ <style>
7
+ /* Specific styling for the documentation content within the main content area */
8
+ .content-padding h2,
9
+ .content-padding h4 {
10
+ color: var(--heading-color); /* Ensure consistent heading colors */
11
+ margin-bottom: 1rem;
12
+ }
13
+
14
+ .content-padding p {
15
+ color: var(--text-color);
16
+ line-height: 1.6;
17
+ margin-bottom: 1rem;
18
+ }
19
+
20
+ .content-padding pre {
21
+ background-color: var(--code-bg);
22
+ color: var(--code-text);
23
+ padding: 1rem;
24
+ border-radius: 0.5rem;
25
+ overflow-x: auto;
26
+ position: relative; /* For copy button positioning */
27
+ margin-bottom: 1.5rem;
28
+ border: 1px solid var(--border);
29
+ }
30
+
31
+ .content-padding code {
32
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
33
+ font-size: 0.9em;
34
+ }
35
+
36
+ .content-padding ul {
37
+ list-style: disc;
38
+ margin-left: 1.5rem;
39
+ margin-bottom: 1rem;
40
+ color: var(--text-color);
41
+ }
42
+
43
+ .content-padding ul li {
44
+ margin-bottom: 0.5rem;
45
+ }
46
+
47
+ .content-padding .text-muted {
48
+ font-size: 0.85rem;
49
+ color: var(--text-muted); /* Define in your variables */
50
+ }
51
+
52
+ /* Copy button specific styles */
53
+ .content-padding .btn.copy-btn-docs {
54
+ position: absolute;
55
+ top: 0.5rem;
56
+ right: 0.5rem;
57
+ padding: 0.3rem 0.7rem;
58
+ font-size: 0.75rem;
59
+ background-color: var(--primary-color);
60
+ color: var(--btn-text-color);
61
+ border: none;
62
+ border-radius: 0.3rem;
63
+ cursor: pointer;
64
+ transition: background-color 0.2s ease;
65
+ z-index: 1; /* Ensure button is above text */
66
+ }
67
+
68
+ .content-padding .btn.copy-btn-docs:hover {
69
+ background-color: var(--primary-hover);
70
+ }
71
+
72
+ /* Form specific styles (reusing .form-group, .form-label, .form-input) */
73
+ .content-padding .form-group {
74
+ margin-bottom: 1.5rem;
75
+ }
76
+ .content-padding textarea.form-input {
77
+ min-height: 120px;
78
+ resize: vertical;
79
+ }
80
+
81
+ /* Table specific styles (reusing .table.table-bordered) */
82
+ .content-padding .table-bordered {
83
+ width: 100%;
84
+ border-collapse: collapse;
85
+ margin-top: 1.5rem;
86
+ }
87
+ .content-padding .table-bordered th,
88
+ .content-padding .table-bordered td {
89
+ border: 1px solid var(--border);
90
+ padding: 0.8rem 1rem;
91
+ text-align: left;
92
+ }
93
+ .content-padding .table-bordered th {
94
+ background-color: var(--table-header-bg);
95
+ color: var(--table-header-text);
96
+ }
97
+ .content-padding .table-bordered tbody tr:nth-child(even) {
98
+ background-color: var(--table-row-even-bg);
99
+ }
100
+ .content-padding .table-bordered tbody tr:hover {
101
+ background-color: var(--table-row-hover-bg);
102
+ }
103
+
104
+ /* Responsive table for API docs */
105
+ @media (max-width: 768px) {
106
+ .content-padding .table-bordered,
107
+ .content-padding .table-bordered tbody,
108
+ .content-padding .table-bordered tr,
109
+ .content-padding .table-bordered td {
110
+ display: block;
111
+ width: 100%;
112
+ }
113
+ .content-padding .table-bordered thead {
114
+ display: none;
115
+ }
116
+ .content-padding .table-bordered tr {
117
+ margin-bottom: 1rem;
118
+ border: 1px solid var(--border);
119
+ border-radius: 0.5rem;
120
+ overflow: hidden;
121
+ }
122
+ .content-padding .table-bordered td {
123
+ border: none;
124
+ border-bottom: 1px solid var(--border-light);
125
+ position: relative;
126
+ padding-left: 50%;
127
+ text-align: right;
128
+ }
129
+ .content-padding .table-bordered td::before {
130
+ content: attr(data-label);
131
+ position: absolute;
132
+ left: 0;
133
+ width: 45%;
134
+ padding-left: 1rem;
135
+ font-weight: 600;
136
+ text-align: left;
137
+ color: var(--text-muted);
138
+ }
139
+ }
140
+
141
+ /* Margin adjustments */
142
+ .content-padding .mt-3 { margin-top: 1rem; }
143
+ .content-padding .mt-4 { margin-top: 1.5rem; }
144
+ .content-padding .mb-4 { margin-bottom: 1.5rem; }
145
+ .content-padding .my-5 { margin-top: 3rem; margin-bottom: 3rem; }
146
+ </style>
147
+ {% endblock %}
148
+
149
+ {% block content %}
150
+ <div class="dashboard-wrapper">
151
+ <aside class="sidebar" id="sidebar">
152
+ <div class="sidebar-header">
153
+ <div class="logo">ResumeRanker<span style="color:var(--accent)">.API</span></div>
154
+ <button class="close-sidebar" id="closeSidebar" aria-label="Close menu">
155
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
156
+ </button>
157
+ </div>
158
+ <nav class="sidebar-nav">
159
+ {# Use Flask's request.endpoint to set 'active' class #}
160
+ <a href="{{ url_for('auth.dashboard') }}" class="nav-item {% if request.endpoint == 'auth.dashboard' %}active{% endif %}">
161
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-grid"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
162
+ <span>Dashboard</span>
163
+ </a>
164
+ <a href="{{ url_for('auth.api_keys') }}" class="nav-item {% if request.endpoint == 'auth.api_keys' %}active{% endif %}">
165
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-key"><path d="M19 10H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2z"></path><circle cx="12" cy="16" r="2"></circle><path d="M15 10V4.5a2.5 2.5 0 0 0-5 0V10"></path></svg>
166
+ <span>My API Keys</span>
167
+ </a>
168
+ <a href="{{ url_for('auth.resume_rankings') }}" class="nav-item {% if request.endpoint == 'auth.resume_rankings' %}active{% endif %}">
169
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-award"><circle cx="12" cy="8" r="7"></circle><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.89"></polyline></svg>
170
+ <span>Resume Rankings</span>
171
+ </a>
172
+ <a href="{{ url_for('auth.try_api') }}" class="nav-item {% if request.endpoint == 'auth.try_api' %}active{% endif %}">
173
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-play-circle"><circle cx="12" cy="12" r="10"></circle><polygon points="10 8 16 12 10 16 10 8"></polygon></svg>
174
+ <span>Try It Now</span>
175
+ </a>
176
+ <a href="{{ url_for('auth.api_docs') }}" class="nav-item {% if request.endpoint == 'auth.api_docs' %}active{% endif %}">
177
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-book"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20V6.5A2.5 2.5 0 0 0 17.5 4h-11A2.5 2.5 0 0 0 4 6.5v13z"></path><path d="M11 6h6"></path><path d="M11 10h6"></path><path d="M11 14h6"></path></svg>
178
+ <span>API Docs</span>
179
+ </a>
180
+ <a href="{{ url_for('auth.settings') }}" class="nav-item {% if request.endpoint == 'auth.settings' %}active{% endif %}">
181
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-settings"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l-.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
182
+ <span>Settings</span>
183
+ </a>
184
+ <a href="{{ url_for('auth.logout') }}" class="nav-item logout-item">
185
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="17 16 22 12 17 8"></polyline><line x1="22" y1="12" x2="10" y2="12"></line></svg>
186
+ <span>Logout</span>
187
+ </a>
188
+ </nav>
189
+ </aside>
190
+
191
+ <div class="main-content-area" id="mainContentArea">
192
+ <header class="dashboard-header">
193
+ <button class="hamburger-dashboard" id="hamburgerDashboard" aria-label="Open sidebar menu">
194
+ <span></span><span></span><span></span>
195
+ </button>
196
+ <div class="user-settings">
197
+ <span class="user-name">{{ user.username }}</span>
198
+ <div class="user-avatar">
199
+ <img src="https://ui-avatars.com/api/?name={{ user.username | urlencode }}&background=2563eb&color=fff&size=30" alt="User Avatar">
200
+ </div>
201
+ <button class="theme-toggle" id="themeToggleDashboard" aria-label="Toggle dark mode">πŸŒ™</button>
202
+ </div>
203
+ </header>
204
+
205
+ <div class="content-padding">
206
+ <h1 class="welcome-title">API Documentation</h1> {# Changed from welcome-title to just a title for the page #}
207
+ <p class="subtitle">Detailed guide on how to integrate and use the Resume Ranker API.</p>
208
+
209
+ {% if error %}
210
+ <div class="error-message">{{ error }}</div>
211
+ {% endif %}
212
+
213
+ {% if success %}
214
+ <div class="success-message">{{ success }}</div>
215
+ {% endif %}
216
+
217
+ <div class="section-card api-docs-section"> {# Wrap documentation in a card for consistent look #}
218
+ <h2 class="mb-4">πŸ“˜ Resume Ranker API Details</h2>
219
+
220
+ <h4>πŸ”— Endpoint</h4>
221
+ <pre><code class="copyable">POST https://rankapply-api.hf.space/rank-resumes</code></pre>
222
+
223
+ <h4>πŸ” Authentication</h4>
224
+ <p>Include your API key in the <code>Authorization</code> header:</p>
225
+ <pre><code class="copyable">Authorization: Bearer your_api_key_here</code></pre>
226
+ <p class="text-muted"><small>Never share your API key publicly. Treat it like a password.</small></p>
227
+
228
+ <h4>Request Parameters</h4>
229
+ <ul>
230
+ <li><strong>resumes</strong> (file[], required): One or more PDF or DOCX resumes.</li>
231
+ <li><strong>job_description</strong> (text, required): The job description to match against.</li>
232
+ </ul>
233
+
234
+ <h4>πŸ“₯ Example Request (cURL)</h4>
235
+ <pre><code class="copyable">curl -X POST https://rankapply-api.hf.space/rank-resumes \
236
+ -H "Authorization: Bearer your_api_key_here" \
237
+ -F "resumes=@cv1.pdf" \
238
+ -F "resumes=@cv2.pdf" \
239
+ -F "job_description=We are hiring a backend engineer..."</code></pre>
240
+
241
+ <h4>πŸ“₯ Example Request (Python)</h4>
242
+ <pre><code class="copyable">import requests
243
+
244
+ headers = {
245
+ "Authorization": "Bearer your_api_key_here"
246
+ }
247
+ files = [
248
+ ('resumes', ('cv1.pdf', open('cv1.pdf', 'rb'), 'application/pdf')),
249
+ ('resumes', ('cv2.pdf', open('cv2.pdf', 'rb'), 'application/pdf')),
250
+ ]
251
+ data = {
252
+ "job_description": "We are hiring a backend engineer..."
253
+ }
254
+
255
+ response = requests.post("https://rankapply-api.hf.space/rank-resumes", headers=headers, files=files, data=data)
256
+ print(response.json())</code></pre>
257
+
258
+ <h4> Sample Response</h4>
259
+ <pre><code class="copyable">{
260
+ "job_description": "We are hiring a backend engineer...",
261
+ "ranked_resumes": [
262
+ {
263
+ "filename": "cv2.pdf",
264
+ "score": 0.8745
265
+ },
266
+ {
267
+ "filename": "cv1.pdf",
268
+ "score": 0.6721
269
+ }
270
+ ]
271
+ }</code></pre>
272
+ </div>
273
+
274
+ <hr class="my-5">
275
+
276
+ <div class="section-card api-tester-form"> {# Reusing the card for the "Try It Now" section #}
277
+ <h3 class="section-title">Try It Now</h3>
278
+ <p class="section-description">Use the form below to quickly test the API directly from your browser.</p>
279
+ <form method="POST" action="{{ url_for('auth.try_api') }}" enctype="multipart/form-data">
280
+ <div class="form-group">
281
+ <label for="api_key" class="form-label">API Key</label>
282
+ <input type="text" class="form-input" id="api_key" name="api_key"
283
+ placeholder="your_api_key_here" value="{{ user.api_key | default('') }}" required>
284
+ </div>
285
+ <div class="form-group">
286
+ <label for="resumes" class="form-label">Upload Resumes (PDF, DOCX)</label>
287
+ <input type="file" class="form-input" id="resumes" name="resumes" multiple accept=".pdf,.doc,.docx" required>
288
+ </div>
289
+ <div class="form-group">
290
+ <label for="job_description" class="form-label">Job Description</label>
291
+ <textarea class="form-input" id="job_description" name="job_description" rows="4" required></textarea>
292
+ </div>
293
+ <button type="submit" class="btn btn-success">Submit Test Request</button>
294
+ </form>
295
+ </div>
296
+
297
+ {% if response %}
298
+ <div class="section-card api-results">
299
+ <h3 class="section-title">πŸ” Resume Ranking Results</h3>
300
+ <p class="section-description"><strong>Job Description:</strong> {{ response.job_description }}</p>
301
+ <div class="table-responsive">
302
+ <table class="table table-bordered">
303
+ <thead>
304
+ <tr>
305
+ <th>Filename</th>
306
+ <th>Score</th>
307
+ </tr>
308
+ </thead>
309
+ <tbody>
310
+ {% for r in response.ranked_resumes %}
311
+ <tr>
312
+ <td data-label="Filename">{{ r.filename }}</td>
313
+ <td data-label="Score">{{ r.score }}</td>
314
+ </tr>
315
+ {% endfor %}
316
+ </tbody>
317
+ </table>
318
+ </div>
319
+ </div>
320
+ {% endif %}
321
+
322
+ <p class="mt-4"><a href="{{ url_for('auth.dashboard') }}" class="btn btn-secondary">← Back to Dashboard</a></p>
323
+ </div>
324
+ </div>
325
+ </div>
326
+ {% endblock %}
327
+
328
+ {% block extra_scripts %}
329
+ <script>
330
+ // Theme toggle (specific to dashboard header)
331
+ const themeToggleDashboard = document.getElementById('themeToggleDashboard');
332
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
333
+ function setTheme(theme) {
334
+ document.documentElement.setAttribute('data-theme', theme);
335
+ localStorage.setItem('theme', theme);
336
+ themeToggleDashboard.textContent = theme === 'dark' ? 'β˜€οΈ' : 'πŸŒ™';
337
+ }
338
+ const savedTheme = localStorage.getItem('theme');
339
+ setTheme(savedTheme || (prefersDark ? 'dark' : 'light'));
340
+ themeToggleDashboard.onclick = () => {
341
+ const current = document.documentElement.getAttribute('data-theme');
342
+ setTheme(current === 'dark' ? 'light' : 'dark');
343
+ };
344
+
345
+ // Sidebar toggle for mobile
346
+ const hamburgerDashboard = document.getElementById('hamburgerDashboard');
347
+ const sidebar = document.getElementById('sidebar');
348
+ const mainContentArea = document.getElementById('mainContentArea');
349
+ const closeSidebarBtn = document.getElementById('closeSidebar');
350
+
351
+ hamburgerDashboard.onclick = () => {
352
+ sidebar.classList.add('open');
353
+ mainContentArea.classList.add('shifted');
354
+ };
355
+ closeSidebarBtn.onclick = () => {
356
+ sidebar.classList.remove('open');
357
+ mainContentArea.classList.remove('shifted');
358
+ };
359
+ mainContentArea.onclick = (event) => {
360
+ if (sidebar.classList.contains('open') && !sidebar.contains(event.target) && !hamburgerDashboard.contains(event.target)) {
361
+ sidebar.classList.remove('open');
362
+ mainContentArea.classList.remove('shifted');
363
+ }
364
+ };
365
+ Array.from(sidebar.getElementsByClassName('nav-item')).forEach(item => {
366
+ item.onclick = () => {
367
+ if (window.innerWidth <= 900) {
368
+ sidebar.classList.remove('open');
369
+ mainContentArea.classList.remove('shifted');
370
+ }
371
+ };
372
+ });
373
+
374
+ // Copy Button Script for code blocks
375
+ document.addEventListener('DOMContentLoaded', function() {
376
+ document.querySelectorAll('pre').forEach(preBlock => {
377
+ const codeBlock = preBlock.querySelector('code.copyable');
378
+ if (codeBlock) {
379
+ const button = document.createElement('button');
380
+ button.innerText = 'Copy';
381
+ button.className = 'btn copy-btn-docs';
382
+ button.addEventListener('click', () => {
383
+ navigator.clipboard.writeText(codeBlock.innerText).then(() => {
384
+ button.innerText = 'Copied!';
385
+ setTimeout(() => button.innerText = 'Copy', 1500);
386
+ }).catch(err => {
387
+ console.error('Failed to copy text: ', err);
388
+ alert('Failed to copy text. Please copy manually.');
389
+ });
390
+ });
391
+ preBlock.appendChild(button);
392
+ }
393
+ });
394
+ });
395
+ </script>
396
+ {% endblock %}
397
+