koprogo_api/application/dto/
individual_member_dto.rs

1// DTOs for Individual Members API (Issue #280)
2
3use crate::domain::entities::IndividualMember;
4use serde::{Deserialize, Serialize};
5use validator::Validate;
6
7/// Request DTO for joining a campaign as individual member
8#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
9pub struct JoinCampaignAsIndividualDto {
10    #[validate(email(message = "Invalid email address"))]
11    pub email: String,
12
13    #[validate(length(min = 4, max = 10, message = "Postal code must be 4-10 characters"))]
14    pub postal_code: String,
15
16    pub annual_consumption_kwh: Option<f64>,
17
18    pub current_provider: Option<String>,
19
20    pub ean_code: Option<String>,
21}
22
23/// Request DTO for granting GDPR consent
24#[derive(Debug, Serialize, Deserialize, Clone)]
25pub struct GrantConsentDto {
26    pub has_consent: bool,
27}
28
29/// Request DTO for updating consumption data
30#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
31pub struct UpdateConsumptionDto {
32    #[validate(range(min = 0.0, message = "Annual consumption must be non-negative"))]
33    pub annual_consumption_kwh: f64,
34
35    pub current_provider: Option<String>,
36
37    pub ean_code: Option<String>,
38}
39
40/// Response DTO for individual member
41#[derive(Debug, Serialize, Deserialize, Clone)]
42pub struct IndividualMemberResponseDto {
43    pub id: String,
44    pub campaign_id: String,
45    pub email: String,
46    pub postal_code: String,
47    pub has_gdpr_consent: bool,
48    pub consent_at: Option<String>,
49    pub annual_consumption_kwh: Option<f64>,
50    pub current_provider: Option<String>,
51    pub ean_code: Option<String>,
52    pub is_active: bool,
53    pub unsubscribed_at: Option<String>,
54    pub created_at: String,
55}
56
57/// Request DTO for unsubscribing from campaign (email token required in URL)
58#[derive(Debug, Serialize, Deserialize, Clone)]
59pub struct UnsubscribeRequestDto {
60    pub confirm: bool,
61}
62
63/// Response DTO for unsubscribe confirmation
64#[derive(Debug, Serialize, Deserialize, Clone)]
65pub struct UnsubscribeConfirmationDto {
66    pub success: bool,
67    pub message: String,
68    pub email: String,
69}
70
71impl From<IndividualMember> for IndividualMemberResponseDto {
72    fn from(member: IndividualMember) -> Self {
73        let is_active = member.is_active();
74        let created_at = member.created_at.to_rfc3339();
75        let unsubscribed_at = member.unsubscribed_at.map(|dt| dt.to_rfc3339());
76        let consent_at = member.consent_at.map(|dt| dt.to_rfc3339());
77        IndividualMemberResponseDto {
78            id: member.id.to_string(),
79            campaign_id: member.campaign_id.to_string(),
80            email: member.email,
81            postal_code: member.postal_code,
82            has_gdpr_consent: member.has_gdpr_consent,
83            consent_at,
84            annual_consumption_kwh: member.annual_consumption_kwh,
85            current_provider: member.current_provider,
86            ean_code: member.ean_code,
87            is_active,
88            unsubscribed_at,
89            created_at,
90        }
91    }
92}