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.