Skip to main content

koprogo_api/application/dto/
notice_dto.rs

1use crate::domain::entities::{Notice, NoticeCategory, NoticeStatus, NoticeType};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6/// DTO for creating a new notice (Draft status)
7#[derive(Debug, Serialize, Deserialize)]
8pub struct CreateNoticeDto {
9    pub building_id: Uuid,
10    pub notice_type: NoticeType,
11    pub category: NoticeCategory,
12    pub title: String,
13    pub content: String,
14    // Event-specific fields (required for Event type)
15    pub event_date: Option<DateTime<Utc>>,
16    pub event_location: Option<String>,
17    // Contact info for LostAndFound and ClassifiedAd
18    pub contact_info: Option<String>,
19    // Optional expiration date
20    pub expires_at: Option<DateTime<Utc>>,
21}
22
23/// DTO for updating a notice (Draft only)
24#[derive(Debug, Serialize, Deserialize)]
25pub struct UpdateNoticeDto {
26    pub title: Option<String>,
27    pub content: Option<String>,
28    pub category: Option<NoticeCategory>,
29    pub event_date: Option<Option<DateTime<Utc>>>,
30    pub event_location: Option<Option<String>>,
31    pub contact_info: Option<Option<String>>,
32    pub expires_at: Option<Option<DateTime<Utc>>>,
33}
34
35/// DTO for setting expiration date
36#[derive(Debug, Serialize, Deserialize)]
37pub struct SetExpirationDto {
38    pub expires_at: Option<DateTime<Utc>>,
39}
40
41/// DTO for archiving a notice.
42///
43/// `reason` est ignoré quand l'auteur archive sa propre annonce, mais devient
44/// obligatoire pour une modération par un tiers (syndic / `community.moderator`
45/// / admin) — Story 5.3 (#587), INV-4.
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47pub struct ArchiveNoticeDto {
48    pub reason: Option<String>,
49}
50
51/// Complete notice response with author information
52#[derive(Debug, Serialize, Clone)]
53pub struct NoticeResponseDto {
54    pub id: Uuid,
55    pub building_id: Uuid,
56    pub author_id: Uuid,
57    pub author_name: String, // Enriched from Owner
58    pub notice_type: NoticeType,
59    pub category: NoticeCategory,
60    pub title: String,
61    pub content: String,
62    pub status: NoticeStatus,
63    pub is_pinned: bool,
64    pub published_at: Option<DateTime<Utc>>,
65    pub expires_at: Option<DateTime<Utc>>,
66    pub archived_at: Option<DateTime<Utc>>,
67    // Event-specific fields
68    pub event_date: Option<DateTime<Utc>>,
69    pub event_location: Option<String>,
70    // Contact info
71    pub contact_info: Option<String>,
72    // Timestamps
73    pub created_at: DateTime<Utc>,
74    pub updated_at: DateTime<Utc>,
75    // Computed fields
76    pub is_expired: bool,
77    pub days_until_event: Option<i64>, // For Event type
78}
79
80impl NoticeResponseDto {
81    /// Create from Notice with author name enrichment
82    pub fn from_notice(notice: Notice, author_name: String) -> Self {
83        let is_expired = notice.is_expired();
84        let days_until_event = if notice.notice_type == NoticeType::Event {
85            notice.event_date.map(|event_date| {
86                let now = Utc::now();
87                (event_date - now).num_days()
88            })
89        } else {
90            None
91        };
92
93        Self {
94            id: notice.id,
95            building_id: notice.building_id,
96            author_id: notice.author_id,
97            author_name,
98            notice_type: notice.notice_type,
99            category: notice.category,
100            title: notice.title,
101            content: notice.content,
102            status: notice.status,
103            is_pinned: notice.is_pinned,
104            published_at: notice.published_at,
105            expires_at: notice.expires_at,
106            archived_at: notice.archived_at,
107            event_date: notice.event_date,
108            event_location: notice.event_location,
109            contact_info: notice.contact_info,
110            created_at: notice.created_at,
111            updated_at: notice.updated_at,
112            is_expired,
113            days_until_event,
114        }
115    }
116}
117
118/// Summary notice response for list views
119#[derive(Debug, Serialize, Clone)]
120pub struct NoticeSummaryDto {
121    pub id: Uuid,
122    pub building_id: Uuid,
123    pub author_name: String,
124    pub notice_type: NoticeType,
125    pub category: NoticeCategory,
126    pub title: String,
127    pub status: NoticeStatus,
128    pub is_pinned: bool,
129    pub published_at: Option<DateTime<Utc>>,
130    pub event_date: Option<DateTime<Utc>>, // For Event type
131    pub created_at: DateTime<Utc>,
132    pub is_expired: bool,
133}
134
135impl NoticeSummaryDto {
136    /// Create from Notice with author name enrichment
137    pub fn from_notice(notice: Notice, author_name: String) -> Self {
138        let is_expired = notice.is_expired();
139
140        Self {
141            id: notice.id,
142            building_id: notice.building_id,
143            author_name,
144            notice_type: notice.notice_type,
145            category: notice.category,
146            title: notice.title,
147            status: notice.status.clone(),
148            is_pinned: notice.is_pinned,
149            published_at: notice.published_at,
150            event_date: notice.event_date,
151            created_at: notice.created_at,
152            is_expired,
153        }
154    }
155}