Skip to content
Open
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
## 2024-11-20 - CopyPromptButton μ ‘κ·Όμ„± ν–₯상 (동적 ν…μŠ€νŠΈ 및 μƒνƒœ)
**Learning:** `CopyPromptButton`κ³Ό 같이 λ²„νŠΌμ„ ν΄λ¦­ν–ˆμ„ λ•Œ μ‹œκ°μ μœΌλ‘œλ§Œ μƒνƒœκ°€ λ³€ν•˜κ³ (예: 볡사 μ•„μ΄μ½˜μ΄ 체크 μ•„μ΄μ½˜μœΌλ‘œ λ³€κ²½), ν…μŠ€νŠΈκ°€ λ™μ μœΌλ‘œ λ³€κ²½λ˜λŠ” μ»΄ν¬λ„ŒνŠΈμ—μ„œλŠ” 슀크린 리더 μ‚¬μš©μžκ°€ μƒνƒœ λ³€ν™”λ₯Ό μ•Œμ•„μ±„κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€. λ˜ν•œ 슀크린 리더가 순수 μž₯μ‹μš© μ•„μ΄μ½˜κΉŒμ§€ λΆˆν•„μš”ν•˜κ²Œ 읽을 수 μžˆμŠ΅λ‹ˆλ‹€.
**Action:** λ™μ μœΌλ‘œ λ³€κ²½λ˜λŠ” ν…μŠ€νŠΈλ₯Ό `<span aria-live="polite">`둜 감싸 슀크린 리더가 μ¦‰μ‹œ λ³€κ²½ 사항을 읽어주도둝 ν•΄μ•Ό ν•©λ‹ˆλ‹€. `<Button>` μ»΄ν¬λ„ŒνŠΈμ—λŠ” `aria-pressed={copied}`λ₯Ό μΆ”κ°€ν•˜μ—¬ ν† κΈ€ 성격을 λΆ€μ—¬ν•˜κ³ , μ‹œκ°μ μΈ μ•„μ΄μ½˜ μ»΄ν¬λ„ŒνŠΈ(예: `<Copy>`, `<Check>`)μ—λŠ” `aria-hidden="true"`λ₯Ό μΆ”κ°€ν•˜μ—¬ 슀크린 λ¦¬λ”μ—μ„œ λ¬΄μ‹œν•˜λ„λ‘ μ²˜λ¦¬ν•˜λŠ” νŒ¨ν„΄μ„ μ§€μ†μ μœΌλ‘œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.
## 2023-10-10 - Async Button Loading States
**Learning:** Text-only loading states ("생성 쀑...") can be less noticeable than expected.
**Action:** Always include a visual spinner (`Loader2` with `animate-spin` and `aria-hidden="true"`) alongside loading text to provide clearer immediate feedback on async actions.
7 changes: 7 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CVE-2026-45819
CVE-2026-73088
CVE-2026-73089
CVE-2026-40345
CVE-2026-75604
GHSA-2xp9-vwfh-vxw4
GHSA-rgj7-g3m4-5g8c
11 changes: 9 additions & 2 deletions packages/web/src/components/admin/admin-login-form.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { useState } from 'react'
import { Lock } from 'lucide-react'
import { Lock, Loader2 } from 'lucide-react'
import { useRouter } from 'next/navigation'

import { Button } from '@/components/ui/button'
Expand Down Expand Up @@ -76,7 +76,14 @@ export function AdminLoginForm() {
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? 'Signing in...' : 'Sign in'}
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
Signing in...
</>
) : (
'Sign in'
)}
</Button>
</form>
</CardContent>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/auth/reset-password-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useState } from 'react'
import Link from 'next/link'
import { Loader2 } from 'lucide-react'

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button, buttonVariants } from '@/components/ui/button'
Expand Down Expand Up @@ -110,7 +111,14 @@ export function ResetPasswordForm({ token, email }: ResetPasswordFormProps) {
</Alert>
)}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? 'Updating...' : 'Update password'}
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
Updating...
</>
) : (
'Update password'
)}
</Button>
</form>
</CardContent>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/org/create-org-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import { useRouter } from 'next/navigation'
import {
AlertDialog,
Expand Down Expand Up @@ -116,7 +117,14 @@ export function CreateOrgModal({ open, onOpenChange }: CreateOrgModalProps) {
size="sm"
disabled={!name.trim() || mutation.isPending}
>
{mutation.isPending ? '생성 쀑…' : '생성'}
{mutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
생성 쀑…
</>
) : (
'생성'
)}
</Button>
</AlertDialogFooter>
</form>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/org/create-project-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import {
AlertDialog,
AlertDialogContent,
Expand Down Expand Up @@ -105,7 +106,14 @@ export function CreateProjectModal({
size="sm"
disabled={!name.trim() || mutation.isPending}
>
{mutation.isPending ? '생성 쀑…' : '생성'}
{mutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
생성 쀑…
</>
) : (
'생성'
)}
</Button>
</AlertDialogFooter>
</form>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/org/delete-org-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import { useRouter } from 'next/navigation'
import {
AlertDialog,
Expand Down Expand Up @@ -113,7 +114,14 @@ export function DeleteOrgModal({
disabled={!canDelete}
onClick={handleDelete}
>
{mutation.isPending ? 'μ‚­μ œ 쀑…' : 'μ‚­μ œ'}
{mutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
μ‚­μ œ 쀑…
</>
) : (
'μ‚­μ œ'
)}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/org/delete-project-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import {
AlertDialog,
AlertDialogContent,
Expand Down Expand Up @@ -113,7 +114,14 @@ export function DeleteProjectModal({
disabled={!canDelete}
onClick={handleDelete}
>
{mutation.isPending ? 'μ‚­μ œ 쀑…' : 'μ‚­μ œ'}
{mutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
μ‚­μ œ 쀑…
</>
) : (
'μ‚­μ œ'
)}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
Expand Down
10 changes: 9 additions & 1 deletion packages/web/src/components/org/rename-project-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useState, useEffect } from 'react'
import { Loader2 } from 'lucide-react'
import {
AlertDialog,
AlertDialogContent,
Expand Down Expand Up @@ -108,7 +109,14 @@ export function RenameProjectModal({
μ·¨μ†Œ
</Button>
<Button type="submit" size="sm" disabled={!canSubmit}>
{mutation.isPending ? 'λ³€κ²½ 쀑…' : 'λ³€κ²½'}
{mutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
λ³€κ²½ 쀑…
</>
) : (
'λ³€κ²½'
)}
</Button>
</AlertDialogFooter>
</form>
Expand Down
Loading