feat(notes): ajout de liens nommés (label + url) sur les notes
Backend : - Migration 006 : colonne urls JSONB nullable sur notes.items - Modèle NoteItem : champ urls list[dict] - Schémas : NoteUrl (label + url avec validation http/https), NoteCreate/NoteUpdate/NoteResponse exposent urls Frontend : - api/notes.ts : interface NoteUrl + champ urls sur Note/NoteCreate - NoteForm : section "Liens" avec ajout (libellé + URL), suppression, validation http/https, confirmation par Enter - NotesPage : badge compteur liens dans metaLine (semi/collapsed), section liens cliquables dans le mode expanded v0.5.13 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
"""006 - ajout colonne urls (JSONB) sur notes.items
|
||||
|
||||
Revision ID: 006
|
||||
Revises: 005
|
||||
Create Date: 2026-05-30
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
|
||||
revision = '006'
|
||||
down_revision = '005'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(
|
||||
'items',
|
||||
sa.Column('urls', JSONB, nullable=True),
|
||||
schema='notes',
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('items', 'urls', schema='notes')
|
||||
@@ -18,6 +18,7 @@ class NoteItem(Base):
|
||||
tags: Mapped[list[str]] = mapped_column(ARRAY(String(50)), server_default=text("'{}'::varchar[]"))
|
||||
gps_lat: Mapped[Decimal | None] = mapped_column(Numeric(10, 7))
|
||||
gps_lon: Mapped[Decimal | None] = mapped_column(Numeric(10, 7))
|
||||
urls: Mapped[list[dict] | None] = mapped_column(JSONB, nullable=True)
|
||||
metadata_: Mapped[dict | None] = mapped_column("metadata", JSONB)
|
||||
created_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), server_default=text("now()"))
|
||||
owner_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, HttpUrl, field_validator
|
||||
|
||||
|
||||
class NoteUrl(BaseModel):
|
||||
label: str
|
||||
url: str
|
||||
|
||||
@field_validator('url')
|
||||
@classmethod
|
||||
def validate_url(cls, v: str) -> str:
|
||||
if not v.startswith(('http://', 'https://')):
|
||||
raise ValueError('URL doit commencer par http:// ou https://')
|
||||
return v
|
||||
|
||||
|
||||
class AttachmentResponse(BaseModel):
|
||||
@@ -20,6 +32,7 @@ class NoteCreate(BaseModel):
|
||||
tags: list[str] = []
|
||||
gps_lat: float | None = None
|
||||
gps_lon: float | None = None
|
||||
urls: list[NoteUrl] = []
|
||||
|
||||
|
||||
class NoteUpdate(BaseModel):
|
||||
@@ -29,6 +42,7 @@ class NoteUpdate(BaseModel):
|
||||
tags: list[str] | None = None
|
||||
gps_lat: float | None = None
|
||||
gps_lon: float | None = None
|
||||
urls: list[NoteUrl] | None = None
|
||||
|
||||
|
||||
class NoteResponse(BaseModel):
|
||||
@@ -40,5 +54,6 @@ class NoteResponse(BaseModel):
|
||||
tags: list[str]
|
||||
gps_lat: float | None
|
||||
gps_lon: float | None
|
||||
urls: list[NoteUrl] = []
|
||||
created_at: datetime
|
||||
attachments: list[AttachmentResponse]
|
||||
|
||||
Reference in New Issue
Block a user