PageSourceSearch

https://www.ia-qa.com/assets/DevToolsheetsPage-BQuKHLFF.js

js ia-qa.com collected 2026-09-24 14:15:13 UTC 33,145 bytes, 482 lines download raw bytes

1import{r as c,j as e,L as f}from"./index-DXHQm1Rr.js";import{H as g}from"./Helmet-B-R13ETi.js";const v=()=>{const[a,m]=c.useState("git"),[o,p]=c.useState(!0),l={git:{name:"Git & Branching",icon:"🌿",color:"#f97316",principles:["Commit early, commit often — small focused commits are easier to review and revert","Commit messages explain WHY, not WHAT — the code shows what","Never force-push to main/master or shared branches","Branch names are descriptive: feat/, fix/, chore/, hotfix/","One logical change per commit — atomic commits enable precise bisect and revert"],dos:["Use Conventional Commits: feat:, fix:, chore:, docs:, refactor:, test:","Pull/rebase before pushing to avoid unnecessary merge commits","Delete merged branches to keep the repo clean","Use --no-ff merges for feature branches to preserve history","Tag releases with semantic versioning: v1.2.3","Write a meaningful PR description: what, why, how to test","Squash commits before merging noisy WIP branches"],donts:['Commit "WIP", "fix", "asdf" without context',"Push directly to main without review","Commit sensitive data (API keys, passwords, .env files)","Create long-lived branches that drift far from main","Merge without reviewing the diff","Use git add . blindly — review what you're staging","Rewrite shared history with rebase or force-push"],templates:[{title:"Conventional Commit Format",template:`<type>(<scope>): <short description>
2
3[optional body]
4Explain the motivation for the change ("why" not "what").
5
6[optional footer]
7BREAKING CHANGE: <description>
8Fixes #<issue-number>
9Closes #<issue-number>
10
11---
12Types: feat | fix | docs | style | refactor | test | chore | perf | ci
13
14Examples:
15feat(auth): add OAuth2 Google login
16fix(cart): prevent negative quantity on decrement
17chore(deps): upgrade react-router to v6.22
18refactor(api): extract pagination into shared hook
19test(checkout): add boundary tests for promo codes
20docs(readme): update local setup instructions
21perf(images): lazy-load product images below fold`},{title:"Git Branching Strategy (Git Flow Lite)",template:`main          ← always deployable, protected
22  │
23  ├── develop   ← integration branch (optional for larger teams)
24  │
25  ├── feat/user-authentication     ← from main/develop
26  ├── feat/payment-v2              ← from main/develop
27  ├── fix/login-redirect-loop      ← from main
28  └── hotfix/xss-in-comment-field  ← from main, merge back to main + develop
29
30Branch naming:
31  feat/[ticket-id]-short-description
32  fix/[ticket-id]-what-is-fixed
33  chore/update-ci-pipeline
34  hotfix/critical-security-patch
35  release/v2.4.0
36
37PR Rules:
38- Target branch: main (or develop)
39- Required: 1-2 reviewers, passing CI
40- Merge strategy: Squash & merge for features, Merge commit for releases`},{title:"Useful Git Commands Cheat Sheet",template:`# Git Cheat Sheet
41
42## Daily Workflow
43git status                          # What changed?
44git diff                            # Review unstaged changes
45git diff --staged                   # Review staged changes
46git add -p                          # Stage changes interactively (recommended over git add .)
47git commit -m "feat(scope): msg"    # Commit with message
48git push origin feat/my-branch      # Push branch
49
50## Undoing Things
51git restore <file>                  # Discard unstaged changes in file
52git restore --staged <file>         # Unstage a file
53git commit --amend --no-edit        # Amend last commit (local only)
54git revert <commit-hash>            # Safe: creates an undo commit
55git reset --hard HEAD~1             # Dangerous: removes last commit permanently
56
57## Branching
58git checkout -b feat/new-feature    # Create + switch to new branch
59git branch -d feat/done             # Delete merged branch locally
60git push origin --delete feat/done  # Delete remote branch
61git merge --no-ff feat/my-feature   # Merge with a merge commit
62git rebase main                     # Rebase current branch onto main
63
64## Investigating
65git log --oneline --graph           # Visual commit history
66git blame <file>                    # Who changed what line?
67git bisect start/good/bad           # Binary search for a regression
68git stash / git stash pop           # Save/restore uncommitted work
69git cherry-pick <commit-hash>       # Apply a specific commit to current branch`}],examples:[{before:'git commit -m "fix stuff"',after:`git commit -m "fix(checkout): prevent double-submit on slow connections
70
71Debounce the submit button and disable it after first click.
72Fixes #874 — duplicate orders were created when network was slow."`},{before:"Working on a feature for 3 weeks on one branch, 200 commits, then merging to main",after:`Break the feature into deliverable sub-tasks:
73→ feat/add-cart-model (merged after sprint 1)
74→ feat/cart-add-remove-items (merged after sprint 2)
75→ feat/cart-checkout-flow (merged after sprint 3)
76Each branch lives max 1 week, reviewed in a focused PR.`}],quickWins:["Set up a commit-msg hook to enforce Conventional Commits format","Add .env, *.log, node_modules to .gitignore before first commit",'Configure git aliases: git config alias.st status, alias.lg "log --oneline --graph"','Use "git add -p" instead of "git add ." to review every change before staging','PR title = commit message that will be squashed: "feat(auth): add Google OAuth"']},"code-review":{name:"Code Review",icon:"🔬",color:"#f97316",principles:["Review the code, not the person — comments are about the code","Author: make the reviewer's job easy — small PRs, good descriptions, self-review first","Reviewer: prioritize correctness and design over style (that's what linters are for)","Distinguish blocking (must fix) vs non-blocking (suggestion/nit) comments","Aim to respond within 24h — slow reviews block your teammates"],dos:["Keep PRs small: < 400 lines changed is reviewable; < 200 is ideal","Write a PR description: what changed, why, how to test it","Self-review your own diff before requesting a review",'Prefix non-blocking comments: "nit:", "suggestion:", "question:"',"Approve with comments — minor nits shouldn't block a merge","Leave positive feedback when code is well-done","Ask follow-up questions to understand intent before critiquing"],donts:["Review more than 1000 lines at once — quality degrades after ~400",'Leave vague comments: "This is wrong" without explaining why',"Nitpick style issues that a linter should catch automatically","Request changes on subjective preferences unless there's a standard","Go silent after submitting a review — follow through on discu
76ssions","Approve without actually reading the diff","Block a PR over a trivial nit that can be fixed in a follow-up"],templates:[{title:"PR Description Template",template:`## What
77Brief description of what this PR does.
78
79## Why
80Context: why is this change needed? Link to ticket/issue.
81
82## How
83Key implementation decisions. Any alternatives considered?
84
85## Testing
86- [ ] Unit tests added / updated
87- [ ] Manual test: [steps]
88- [ ] Edge cases covered: [list]
89
90## Screenshots (if UI change)
91| Before | After |
92|--------|-------|
93| [img]  | [img] |
94
95## Checklist
96- [ ] Self-reviewed the diff
97- [ ] No debug/temp code left in
98- [ ] No console.log or commented-out code
99- [ ] Environment variables documented
100- [ ] Migrations / breaking changes noted`},{title:"Code Review Checklist",template:`## Code Review Checklist
101
102### Correctness
103- [ ] Logic is correct — does what it's supposed to do
104- [ ] Edge cases handled: null, empty, zero, max values
105- [ ] Error handling is present and meaningful
106- [ ] No silent failures or swallowed exceptions
107
108### Security
109- [ ] No SQL injection risk (parameterized queries used)
110- [ ] No XSS risk (user input sanitized / escaped)
111- [ ] No sensitive data in logs, error messages, or responses
112- [ ] Auth/access control checks present where needed
113- [ ] No hardcoded credentials or secrets
114
115### Performance
116- [ ] No N+1 query problem
117- [ ] Appropriate indexes exist for new DB queries
118- [ ] No unnecessary re-renders (React: useMemo/useCallback where needed)
119- [ ] Large data sets handled with pagination/streaming
120
121### Maintainability
122- [ ] Functions do one thing (SRP)
123- [ ] Names are clear and intention-revealing
124- [ ] No magic numbers — use named constants
125- [ ] Complex logic has a comment explaining "why"
126- [ ] Dead code removed
127
128### Tests
129- [ ] New behavior has test coverage
130- [ ] Tests test behavior, not implementation
131- [ ] No test that only tests mocks`},{title:"Reviewer Comment Templates",template:`# Blocking (must fix before merge)
132"This will cause [specific issue] when [scenario]. Please [fix action].
133See: [reference link]"
134
135# Non-blocking suggestion
136"suggestion: consider extracting this into a separate function —
137it would make [reason]. Happy with either approach though."
138
139# Question (not blocking)
140"question: what happens here if [edge case]? Do we need a guard?
141Just want to make sure we've thought about it."
142
143# Nitpick (tone clearly non-blocking)
144"nit: minor but \`userID\` would be more consistent with the rest
145of the codebase. Up to you."
146
147# Positive feedback
148"Nice approach! Using [pattern] here makes this much easier
149to test than [alternative]."
150
151# Request for explanation
152"I'm not sure I follow the logic here — could you add a brief
153comment explaining why [approach] rather than [simpler approach]?"`}],examples:[{before:"This is wrong",after:'"blocking: This will silently swallow errors if fetchUser() rejects — the catch block below only handles the JSON parse error, not network failures. Consider wrapping the entire try/catch higher up or using a centralized error handler."'},{before:'PR: "Refactor auth + fix cart + update styles + add user settings" (1200 lines)',after:`Split into 4 PRs:
1541. refactor: extract auth logic to useAuth hook (200 lines)
1552. fix(cart): prevent negative quantity (50 lines)
1563. style(dashboard): apply new design tokens (150 lines)
1574. feat(settings): add user preferences page (400 lines)
158Each reviewed independently, merged in order.`}],quickWins:["Set up PR templates in .github/PULL_REQUEST_TEMPLATE.md",'Use "Suggested changes" in GitHub to propose exact code fixes','Review your own diff in "Files changed" before submitting the PR',"Use a linter/formatter to remove style debates from code review",'"Approve with comments" on trivial nits to keep velocity up']},"clean-code":{name:"Clean Code",icon:"✨",color:"#10b981",principles:["Code is read 10x more than it's written — optimize for readability",'Functions do one thing — if you need "and" to describe it, split it',"Names explain intent: getActiveUsers() not getData()","Comments explain WHY, not WHAT — code should be self-documenting","The best code is code that doesn't need to be written"],dos:["Use descriptive variable and function names (no abbreviations)","Keep functions short — aim for < 20 lines, max 50","Extract magic numbers into named constants","Use early returns to reduce nesting (guard clauses)","Keep the level of abstraction consistent within a function","Prefer immutability — avoid mutating function arguments","Write code for the next developer, not just for the computer"],donts:["Use single-letter variables outside of short loops","Write functions longer than a screen (50+ lines)","Deep nesting: if (a) { if (b) { if (c) { ... } } }","Return different types from the same function","Pass boolean flags as parameters — split into two functions","Leave TODO comments without a ticket or owner","Copy-paste code — extract the duplication into a function"],templates:[{title:"Naming Conventions by Type",template:`## Naming Conventions
159
160### Functions / Methods
161✓ Verb + domain noun:
162  getUserById(id)         → fetch single record
163  calculateTotalPrice()   → computation
164  validateEmailFormat()   → validation
165  formatDateToISO()       → transformation
166  handleSubmitForm()      → event handler
167  isEmailValid()          → boolean (is/has/can/should prefix)
168  hasActiveSubscription() → boolean
169
170### Variables
171  const userList = []          → collection (plural or List/Map/Set)
172  const currentUser = {}       → single entity
173  const MAX_RETRIES = 3        → constant (SCREAMING_SNAKE_CASE)
174  const isLoading = false      → boolean (is/has/can)
175  const error = null           → nullable value (avoid 'e')
176
177### Components (React)
178  UserProfileCard              → PascalCase, descriptive
179  ProductListPage              → suffix: Page, Card, Modal, Form
180  useAuthStatus                → hooks: use prefix
181
182### Files
183  user-profile.service.ts      → kebab-case for files
184  UserProfile.tsx              → PascalCase for React components
185  auth.utils.ts                → domain + type suffix`},{title:"Guard Clauses Pattern",template:`// ❌ Deep nesting (hard to read)
186function processOrder(order) {
187  if (order) {
188    if (order.items.length > 0) {
189      if (order.user.isActive) {
190        if (order.total > 0) {
191          // actual logic here, buried 4 levels in
192          return processPayment(order);
193        }
194      }
195    }
196  }
197}
198
199// ✅ Guard clauses (early returns)
200function processOrder(order) {
201  if (!order)                    throw new Error('Order is required');
202  if (order.items.length === 0) throw new Error('Order must have items');
203  if (!order.user.isActive)     throw new Error('User account is inactive');
204  if (order.total <= 0)         throw new Error('Order total must be positive');
205
206  // happy path — no nesting, easy to read
207  return processPayment(order);
208}`},{title:"Function Design Rules",template:`## Function Design Checklist
209
210# 1. Single Responsibility
211❌ function saveUserAndSendEmail(user) { /* does 2 things */ }
212✅ function saveUser(user) { ... }
213✅ function sendWelcomeEmail(user) { ... }
214
215# 2. No Boolean Flag Parameters
216❌ function getUsers(includeInactive) { ... }
217✅ function getActiveUsers() { ... }
218✅ function getAllUsers() { ... }
219
220# 3. No Side Effects in Queries
221❌ function getUserCount() { db.log(); return db.count(); }
222✅ function getUserCount() { return db.count(); }
223Void functions: logging, saving, side effects — explicit names
224
225# 4. Error Handling
226❌ function fetchData() { try { ... } catch (e) {} }  // silent swallow
227✅ function fetchData() {
228  try { ... }
229  catch (e) { logger.error('fetchData failed:', e); throw e; }
230}
231
232# 5. Max 3 Parameters (use object for more)
233❌ createOrder(userId, items, discount, shipping, promo)
234✅ createOrder({ userId, items, discount, shipping, promo })`}],examples:[{before:`function calc(a, b, c, t) {
235  let r;
236  if (t === 1) r = a + b;
237  else if (t === 2) r = a - b;
238  if (r > c) return true;
239  return false;
240}`,after:`const OPERATION = { ADD: 'add', SUBTRACT: 'subtract' };
241
242function exceedsThreshold({ valueA, valueB, threshold, operation }) {
243  const result = operation === OPERATION.ADD
244    ? valueA + valueB
245    : valueA - valueB;
246  return result > threshold;
247}`},{before:`useEffect(() => {
248  // TODO: fix this
249  if (data) {
250    if (data.items) {
251      if (data.items.length) {
252        setItems(data.items)
253      }
254    }
255  }
256}, [data])`,after:`useEffect(() => {
257  const items = data?.items;
258  if (items?.length) setItems(items);
259}, [data]);`}],quickWins:["Run a linter on every save — automate style enforcement","Refactor: if you need a comment to explain code, rewrite the code",'The "Rule of Three": duplicate once is OK, duplicate three times → extract',"Use const by default, let only when reassignment is needed, never var","Boy Scout Rule: leave the code cleaner than you found it"]},"backend-patterns":{name:"Backend Patterns",icon:"⚙️",color:"#0ea5e9",principles:["Validate at the boundary — never trust external input","Fail fast, fail loudly — silent errors cause ghost bugs","Idempotent operations prevent duplicate-action bugs","Separate reads from writes (CQRS lite) for clarity","Don't expose internal error details in API responses"],dos:["Validate and sanitize all user input at the API entry point","Return consistent error response shapes across all endpoints","Use database transactions for multi-step mutations","Paginate list endpoints — never return unbounded arrays","Log with correlation IDs to trace requests across services","Handle retries with exponential backoff and jitter","Version your APIs: /api/v1/, /api/v2/"],donts:["Return raw database errors or stack traces to the client","Perform heavy computation in synchronous request handlers","Store sensitive data in query strings (they appear in logs)","Use magic strings — define status values as constants/enums",'Skip input validation trusting "the frontend validates it"',"Do N+1 queries in loops (load relations with JOINs or includes)","Ignore database query execution plans for slow queries"],templates:[{title:"REST API Error Standard",template:`## Standard Error Response Shape
260
261// All errors must follow this shape:
262{
263  "error": {
264    "code": "VALIDATION_ERROR",      // machine-readable code (string enum)
265    "message": "Email is invalid",    // human-readable message
266    "field": "email",                 // optional: relevant field
267    "details": [...]                  // optional: extra context
268  },
269  "requestId": "req_abc123"          // for log correlation
270}
271
272// HTTP Status Code Guide:
273200 OK          → successful read
274201 Created     → successful create
275204 No Content  → successful delete/update with no body
276400 Bad Request → validation error, malformed request
277401 Unauthorized → missing or invalid auth
278403 Forbidden   → valid auth, insufficient permissions
279404 Not Found   → resource does not exist
280409 Conflict    → duplicate, version conflict
281422 Unprocessable → valid JSON but semantic error
282429 Too Many Requests → rate limit exceeded
283500 Internal Server Error → unexpected server error`},{title:"Service Layer Pattern",template:`// Controller → Service → Repository pattern
284
285// ✅ Controller: HTTP concerns only
286class UserController {
287  async createUser(req, res) {
288    try {
289      const dto = validateCreateUserDto(req.body);  // validate at boundary
290      const user = await userService.createUser(dto);
291      res.status(201).json({ data: user });
292    } catch (err) {
293      next(err);  // pass to error middleware
294    }
295  }
296}
297
298// ✅ Service: business logic
299class UserService {
300  async createUser(dto) {
301    const exists = await userRepo.findByEmail(dto.email);
302    if (exists) throw new ConflictError('Email already registered');
303
304    const hashedPassword = await hashPassword(dto.password);
305    const user = await userRepo.create({ ...dto, password: hashedPassword });
306    await emailService.sendWelcome(user);
307    return user;
308  }
309}
310
311// ✅ Repository: data access only
312class UserRepository {
313  async findByEmail(email) {
314    return db.users.findFirst({ where: { email } });
315  }
316}`},{title:"Database Query Best Practices",template:`## DB Query Checklist
317
318# N+1 Problem — use eager loading
319❌ const orders = await Order.findAll();
320   for (const order of orders) {
321     order.user = await User.findById(order.userId);  // N queries!
322   }
323
324✅ const orders = await Order.findAll({
325     include: [{ model: User }]  // 1 JOIN query
326   });
327
328# Pagination — never return unbounded lists
329❌ SELECT * FROM products;
330✅ SELECT * FROM products
331   ORDER BY created_at DESC
332   LIMIT :limit OFFSET :offset;
333
334# Indexes — add them before the query is slow in production
335-- Index every foreign key
336-- Index columns used in WHERE, ORDER BY, GROUP BY
337CREATE INDEX idx_orders_user_id ON orders(user_id);
338CREATE INDEX idx_products_category_created ON products(category_id, created_at DESC);
339
340# Transactions — wrap multi-step mutations
341await db.transaction(async (t) => {
342  const order = await Order.create(orderData, { transaction: t });
343  await Stock.decrement({ quantity: qty }, { where: { id }, transaction: t });
344  // Rolled back automatically if any step throws
345});`}],examples:[{before:`router.post('/users', async (req, res) => {
346  const user = await db.save(req.body);
347  res.json(user);
348})`,after:`router.post('/users', async (req, res, next) => {
349  try {
350    // 1. Validate
351    const dto = validateCreateUserDto(req.body);
352    // 2. Business logic in service layer
353    const user = await userService.createUser(dto);
354    // 3. Return only what client needs (no password hash)
355    res.status(201).json({ data: sanitizeUser(user) });
356  } catch (err) {
357    next(err); // centralized error handler
358  }
359});`},{before:`catch (error) {
360  res.status(500).json({ error: error.message, stack: error.stack });
361}`,after:`// Centralized error middleware
362app.use((err, req, res, next) => {
363  const statusCode = err.statusCode || 500;
364  logger.error({ err, requestId: req.id }); // log internally
365  res.status(statusCode).json({
366    error: {
367      code: err.code || 'INTERNAL_ERROR',
368      message: statusCode < 500 ? err.message : 'An unexpected error occurred',
369      requestId: req.id
370    }
371  }); // never expose stack trace to client
372});`}],quickWins:["Use a request validation library (Zod, Joi, Yup) — don't validate by hand","Add request IDs (UUID) at the gateway level for log correlation","Set up health check endpoints: /health, /ready, /live","Use environment variables for all c
372onfig — never hardcode values","Enable query logging in dev to catch N+1 problems early"]}},r=l[a],u=()=>{const t=`# Dev Toolsheet: ${r.name}
373
374## Core Principles
375${r.principles.map((s,d)=>`${d+1}. ${s}`).join(`
376`)}
377
378## Do's
379${r.dos.map(s=>`✓ ${s}`).join(`
380`)}
381
382## Don'ts
383${r.donts.map(s=>`✗ ${s}`).join(`
384`)}
385
386## Templates
387${r.templates.map(s=>`
388### ${s.title}
389\`\`\`
390${s.template}
391\`\`\``).join(`
392`)}
393
394## Examples
395${r.examples.map(s=>`
396Before:
397${s.before}
398
399After:
400${s.after}`).join(`
401
402`)}
403
404## Quick Wins
405${r.quickWins.map((s,d)=>`${d+1}. ${s}`).join(`
406`)}
407`,n=new Blob([t],{type:"text/markdown"}),h=URL.createObjectURL(n),i=document.createElement("a");i.href=h,i.download=`Dev_Toolsheet_${r.name.replace(/\s+/g,"_")}.md`,i.click()};return e.jsxs("div",{className:"tool-page",children:[e.jsxs(g,{children:[e.jsx("title",{children:"Dev Toolsheets — Git, Code Review, Clean Code, Backend | IA-QA"}),e.jsx("meta",{name:"description",content:"Developer cheat sheets for Git workflows, code review best practices, clean code principles, and backend patterns. Templates and examples for dev teams."}),e.jsx("meta",{name:"keywords",content:"dev toolsheet, git cheat sheet, code review checklist, clean code, backend patterns, REST API design, developer best practices"}),e.jsx("link",{rel:"canonical",href:"https://www.ia-qa.com/devtools/dev-toolsheets"})]}),e.jsx("style",{children:`
408        .ts-container { max-width: 1200px; margin: 0 auto; padding: 20px; }
409        .ts-back { display: inline-flex; align-items: center; gap: 6px; color: #f97316; text-decoration: none; font-weight: 500; margin-bottom: 20px; }
410        .ts-back:hover { text-decoration: underline; }
411        .ts-header-dev { background: linear-gradient(135deg, #09090b 0%, #27272a 100%); border-radius: 16px; padding: 40px; margin-bottom: 24px; text-align: center; }
412        .ts-header-dev h1 { color: white; font-size: 2.2rem; margin: 0 0 8px 0; }
413        .ts-header-dev p { color: rgba(255,255,255,0.75); font-size: 1.05rem; margin: 0; }
414        .ts-tabs { display: flex; gap: 10px; flex-wrap: wrap; background: #f8fafc; border-radius: 12px; padding: 16px; margin-bottom: 24px; }
415        .ts-tab { padding: 10px 20px; border-radius: 8px; border: 2px solid #e2e8f0; background: white; cursor: pointer; font-size: 0.95rem; font-weight: 500; transition: all 0.2s; display: flex; align-items: center; gap: 8px; }
416        .ts-tab:hover { border-color: #94a3b8; background: #f1f5f9; }
417        .ts-tab.active { color: white; border-color: transparent; }
418        .ts-card { background: white; border-radius: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); overflow: hidden; }
419        .ts-card-header { padding: 24px 30px; border-bottom: 1px solid #f0f4f8; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; }
420        .ts-card-title { display: flex; align-items: center; gap: 12px; }
421        .ts-icon { font-size: 2.5rem; }
422        .ts-name { font-size: 1.75rem; font-weight: 700; }
423        .ts-subtitle { color: #6b7280; font-size: 0.95rem; margin-top: 2px; }
424        .ts-actions { display: flex; gap: 10px; }
425        .ts-btn { padding: 10px 20px; border-radius: 8px; border: none; cursor: pointer; font-size: 0.9rem; font-weight: 600; transition: all 0.2s; }
426        .ts-btn-toggle { background: #f1f5f9; color: #374151; }
427        .ts-btn-toggle:hover { background: #e2e8f0; }
428        .ts-btn-download { background: linear-gradient(135deg, #f97316, #ea580c); color: white; }
429        .ts-btn-download:hover { opacity: 0.9; transform: translateY(-1px); }
430        .ts-section { padding: 24px 30px; border-bottom: 1px solid #f0f4f8; }
431        .ts-section:last-child { border-bottom: none; }
432        .ts-section-title { font-size: 1.15rem; font-weight: 700; margin: 0 0 16px 0; color: #09090b; }
433        .ts-principles { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
434        .ts-principles li { background: #f0f4ff; border-left: 3px solid #f97316; border-radius: 6px; padding: 10px 14px; color: #374151; font-size: 0.95rem; }
435        .ts-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
436        .ts-dos, .ts-donts { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
437        .ts-dos li { background: #f0fdf4; border-left: 3px solid #22c55e; border-radius: 6px; padding: 8px 12px; color: #374151; font-size: 0.9rem; }
438        .ts-donts li { background: #fef2f2; border-left: 3px solid #ef4444; border-radius: 6px; padding: 8px 12px; color: #374151; font-size: 0.9rem; }
439        .ts-templates { display: flex; flex-direction: column; gap: 16px; }
440        .ts-template { border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; }
441        .ts-template-title { background: #f8fafc; padding: 10px 16px; font-weight: 600; font-size: 0.95rem; color: #09090b; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center; }
442        .ts-template-body { background: #09090b; color: #e2e8f0; padding: 16px; font-family: monospace; font-size: 0.82rem; line-height: 1.6; white-space: pre-wrap; overflow-x: auto; }
443        .ts-copy-btn { font-size: 0.8rem; padding: 3px 10px; border-radius: 6px; border: none; background: #f97316; color: white; cursor: pointer; font-weight: 600; }
444        .ts-examples { display: flex; flex-direction: column; gap: 16px; }
445        .ts-example { border: 1px solid #fde68a; background: #fffbeb; border-radius: 10px; padding: 16px; }
446        .ts-example-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
447        .ts-ex-label { font-size: 0.8rem; font-weight: 700; margin-bottom: 6px; }
448        .ts-ex-before .ts-ex-label { color: #dc2626; }
449        .ts-ex-after .ts-ex-label { color: #16a34a; }
450        .ts-ex-text { background: white; border-radius: 6px; padding: 10px 12px; font-size: 0.82rem; font-family: monospace; white-space: pre-line; color: #374151; }
451        .ts-quickwins { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
452        .ts-quickwins li { background: #fff7ed; border-left: 3px solid #f59e0b; border-radius: 6px; padding: 10px 14px; color: #374151; font-size: 0.9rem; }
453        .ts-quickwins li::before { content: '⚡ '; }
454
455        @media (max-width: 768px) {
456          .ts-header-dev h1 { font-size: 1.6rem; }
457          .ts-grid-2 { grid-template-columns: 1fr; }
458          .ts-example-row { grid-template-columns: 1fr; }
459          .ts-card-header { flex-direction: column; align-items: flex-start; }
460          .ts-actions { width: 100%; flex-direction: column; }
461          .ts-btn { width: 100%; }
462        }
463
464        .dark-mode .ts-header-dev { background: rgba(9,9,11,0.8) !important; border: 1px solid rgba(255,255,255,0.1); }
465        .dark-mode .ts-tabs { background: rgba(255,255,255,0.05) !important; }
466        .dark-mode .ts-tab { background: rgba(255,255,255,0.06) !important; border-color: rgba(255,255,255,0.12) !important; color: rgba(255,255,255,0.8) !important; }
467        .dark-mode .ts-tab:hover { background: rgba(255,255,255,0.10) !important; }
468        .dark-mode .ts-card { background: rgba(255,255,255,0.06) !important; box-shadow: 0 4px 20px rgba(0,0,0,0.4) !important; }
469        .dark-mode .ts-card-header { border-bottom-color: rgba(255,255,255,0.1) !important; }
470        .dark-mode .ts-name { color: #f9fafb !important; }
471        .dark-mode .ts-section { border-bottom-color: rgba(255,255,255,0.08) !important; }
472        .dark-mode .ts-section-title { color: #f9fafb !important; }
473        .dark-mode .ts-principles li { background: rgba(249,115,22,0.15) !important; color: rgba(255,255,255,0.85) !important; }
474        .dark-mode .ts-dos li { background: rgba(34,197,94,0.12) !important; color: rgba(255,255,255,0.85) !important; }
475        .dark-mode .ts-donts li { background: rgba(239,68,68,0.12) !important; color: rgba(255,255,255,0.85) !important; }
476        .dark-mode .ts-template { border-color: rgba(255,255,255,0.1) !important; }
477        .dark-mode .ts-template-title { background: rgba(255,255,255,0.06) !important; color: #f9fafb !important; border-bottom-color: rgba(255,255,255,0.1) !important; }
478        .dark-mode .ts-example { background: rgba(245,158,11,0.08) !important; border-color: rgba(251,191,36,0.25) !important; }
479        .dark-mode .ts-ex-text { background: rgba(255,255,255,0.06) !important; color: rgba(255,255,255,0.8) !important; }
480        .dark-mode .ts-quickwins li { background: rgba(245,158,11,0.1) !important; color: rgba(255,255,255,0.85) !important; }
481        .dark-mode .ts-btn-toggle { background: rgba(255,255,255,0.1) !important; color: #f9fafb !important; }
482      `}),e.jsxs("div",{className:"ts-container",children:[e.jsx(f,{to:"/home",className:"ts-back",children:"← Back to Home"}),e.jsxs("div",{className:"ts-header-dev",children:[e.jsx("h1",{children:"👨‍💻 Dev Toolsheets"}),e.jsx("p",{children:"Git workflows, code review, clean code, and backend patterns — cheat sheets for developers"})]}),e.jsx("div",{className:"ts-tabs",children:Object.entries(l).map(([t,n])=>e.jsxs("button",{className:`ts-tab ${a===t?"active":""}`,style:a===t?{background:n.color,borderColor:n.color}:{},onClick:()=>m(t),children:[e.jsx("span",{children:n.icon})," ",n.name]},t))}),e.jsxs("div",{className:"ts-card",children:[e.jsxs("div",{className:"ts-card-header",children:[e.jsxs("div",{className:"ts-card-title",children:[e.jsx("div",{className:"ts-icon",children:r.icon}),e.jsxs("div",{children:[e.jsx("div",{className:"ts-name",style:{color:r.color},children:r.name}),e.jsx("div",{className:"ts-subtitle",children:"Dev Toolsheet"})]})]}),e.jsxs("div",{className:"ts-actions",children:[e.jsxs("button",{className:"ts-btn ts-btn-toggle",onClick:()=>p(!o),children:[o?"Hide":"Show"," Examples"]}),e.jsx("button",{className:"ts-btn ts-btn-download",onClick:u,children:"💾 Download .md"})]})]}),e.jsxs("div",{className:"ts-section",children:[e.jsx("div",{className:"ts-section-title",children:"✨ Core Principles"}),e.jsx("ul",{className:"ts-principles",children:r.principles.map((t,n)=>e.jsx("li",{children:t},n))})]}),e.jsx("div",{className:"ts-section",children:e.jsxs("div",{className:"ts-grid-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"ts-section-title",children:"✓ Do's"}),e.jsx("ul",{className:"ts-dos",children:r.dos.map((t,n)=>e.jsx("li",{children:t},n))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"ts-section-title",children:"✗ Don'ts"}),e.jsx("ul",{className:"ts-donts",children:r.donts.map((t,n)=>e.jsx("li",{children:t},n))})]})]})}),e.jsxs("div",{className:"ts-section",children:[e.jsx("div",{className:"ts-section-title",children:"📝 Templates & Cheat Sheets"}),e.jsx("div",{className:"ts-templates",children:r.templates.map((t,n)=>e.jsxs("div",{className:"ts-template",children:[e.jsxs("div",{className:"ts-template-title",children:[t.title,e.jsx("button",{className:"ts-copy-btn",onClick:()=>navigator.clipboard.writeText(t.template),children:"Copy"})]}),e.jsx("div",{className:"ts-template-body",children:t.template})]},n))})]}),o&&e.jsxs("div",{className:"ts-section",children:[e.jsx("div",{className:"ts-section-title",children:"💡 Before / After Examples"}),e.jsx("div",{className:"ts-examples",children:r.examples.map((t,n)=>e.jsx("div",{className:"ts-example",children:e.jsxs("div",{className:"ts-example-row",children:[e.jsxs("div",{className:"ts-ex-before",children:[e.jsx("div",{className:"ts-ex-label",children:"❌ Before"}),e.jsx("div",{className:"ts-ex-text",children:t.before})]}),e.jsxs("div",{className:"ts-ex-after",children:[e.jsx("div",{className:"ts-ex-label",children:"✅ After"}),e.jsx("div",{className:"ts-ex-text",children:t.after})]})]})}
482,n))})]}),e.jsxs("div",{className:"ts-section",children:[e.jsx("div",{className:"ts-section-title",children:"⚡ Quick Wins"}),e.jsx("ul",{className:"ts-quickwins",children:r.quickWins.map((t,n)=>e.jsx("li",{children:t},n))})]})]})]})]})};export{v as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.