Skip to content
57 changes: 57 additions & 0 deletions src/elements/public/StoreForm/StoreForm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2469,4 +2469,61 @@ describe('StoreForm', () => {

expect(element.errors).to.include('error:store_domain_exists');
});
it('produces the auto-anonymize-days:v8n_too_small error when the value is under 90', () => {
const form = new Form();
expect(form.errors).to.not.include('auto-anonymize-days:v8n_too_small');

form.edit({ data_retention: { auto_anonymize: false, auto_anonymize_days: 89 } });
expect(form.errors).to.include('auto-anonymize-days:v8n_too_small');

form.edit({ data_retention: { auto_anonymize: false, auto_anonymize_days: 90 } });
expect(form.errors).to.not.include('auto-anonymize-days:v8n_too_small');
});

it('produces the auto-anonymize-days:v8n_required error when auto-anonymization is on but days are unset', () => {
const form = new Form();
expect(form.errors).to.not.include('auto-anonymize-days:v8n_required');

form.edit({ data_retention: { auto_anonymize: true, auto_anonymize_days: null } });
expect(form.errors).to.include('auto-anonymize-days:v8n_required');

form.edit({ data_retention: { auto_anonymize: true, auto_anonymize_days: 365 } });
expect(form.errors).to.not.include('auto-anonymize-days:v8n_required');
});

it('renders a switch control for auto-anonymization in the Data Retention section', async () => {
const element = await fixture<Form>(html`<foxy-store-form></foxy-store-form>`);
const control = element.renderRoot.querySelector(
'[infer="data-retention"] foxy-internal-switch-control[infer="auto-anonymize"]'
) as InternalSwitchControl;

expect(control).to.exist;

element.edit({ data_retention: { auto_anonymize: true, auto_anonymize_days: 120 } });
expect(control.getValue()).to.equal(true);

control.setValue(false);
expect(element).to.have.nested.property('form.data_retention.auto_anonymize', false);
expect(element).to.have.nested.property('form.data_retention.auto_anonymize_days', 120);
});

it('renders a number control for days of inactivity in the Data Retention section when auto-anonymization is on', async () => {
const element = await fixture<Form>(html`<foxy-store-form></foxy-store-form>`);
const selector =
'[infer="data-retention"] foxy-internal-number-control[infer="auto-anonymize-days"]';

expect(element.renderRoot.querySelector(selector)).to.not.exist;

element.edit({ data_retention: { auto_anonymize: true, auto_anonymize_days: 365 } });
await element.requestUpdate();

const control = element.renderRoot.querySelector(selector) as InternalNumberControl;

expect(control).to.exist;
expect(control).to.have.attribute('min', '90');
expect(control.getValue()).to.equal(365);

control.setValue(120);
expect(element).to.have.nested.property('form.data_retention.auto_anonymize_days', 120);
});
});
60 changes: 60 additions & 0 deletions src/elements/public/StoreForm/StoreForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Rels } from '@foxy.io/sdk/backend';

import type {
ParsedCustomDisplayIdConfig,
ParsedDataRetention,
ParsedSmtpConfig,
ParsedWebhookKey,
Data,
Expand Down Expand Up @@ -164,6 +165,18 @@ export class StoreForm extends Base<Data> {
({ unified_order_entry_password: v }) => {
return !v || String(v).length <= 100 || 'unified-order-entry-password:v8n_too_long';
},

({ data_retention: v }) => {
const days = v?.auto_anonymize_days;
if (days === null || days === undefined) return true;
return (Number.isInteger(days) && days >= 90) || 'auto-anonymize-days:v8n_too_small';
},

({ data_retention: v }) => {
if (!v?.auto_anonymize) return true;
const days = v.auto_anonymize_days;
return (typeof days === 'number' && days >= 90) || 'auto-anonymize-days:v8n_required';
},
];
}

Expand Down Expand Up @@ -288,6 +301,31 @@ export class StoreForm extends Base<Data> {
this.edit({ store_name: newValue });
};

private readonly __getDataRetention = (): ParsedDataRetention => {
const config = this.form.data_retention;

return {
auto_anonymize: config?.auto_anonymize ?? false,
auto_anonymize_days: config?.auto_anonymize_days ?? null,
};
};

private readonly __getAutoAnonymizeValue = (): boolean => {
return this.__getDataRetention().auto_anonymize;
};

private readonly __setAutoAnonymizeValue = (newValue: boolean) => {
this.edit({ data_retention: { ...this.__getDataRetention(), auto_anonymize: newValue } });
};

private readonly __getAutoAnonymizeDaysValue = (): number | null => {
return this.__getDataRetention().auto_anonymize_days;
};

private readonly __setAutoAnonymizeDaysValue = (newValue: number) => {
this.edit({ data_retention: { ...this.__getDataRetention(), auto_anonymize_days: newValue } });
};

private readonly __getStoreEmailValue = (): Item[] => {
const emails = this.form.store_email ?? '';
return emails
Expand Down Expand Up @@ -1017,6 +1055,28 @@ export class StoreForm extends Base<Data> {
: ''}
</foxy-internal-summary-control>

<foxy-internal-summary-control infer="data-retention">
<foxy-internal-switch-control
infer="auto-anonymize"
.getValue=${this.__getAutoAnonymizeValue}
.setValue=${this.__setAutoAnonymizeValue}
>
</foxy-internal-switch-control>
${this.form.data_retention?.auto_anonymize
? html`
<foxy-internal-number-control
layout="summary-item"
infer="auto-anonymize-days"
min="90"
step="1"
.getValue=${this.__getAutoAnonymizeDaysValue}
.setValue=${this.__setAutoAnonymizeDaysValue}
>
</foxy-internal-number-control>
`
: ''}
</foxy-internal-summary-control>

${this.renderTemplateOrSlot()}
${this.href || !this.hCaptchaSiteKey
? ''
Expand Down
5 changes: 5 additions & 0 deletions src/elements/public/StoreForm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ export type Data = Omit<Resource<Rels.Store>, 'custom_display_id_config'> & {
export type ParsedWebhookKey = StoreWebhookKeyJson;
export type ParsedSmtpConfig = StoreSmtpConfigJson;
export type ParsedCustomDisplayIdConfig = StoreCustomDisplayIdConfigJson;

export type ParsedDataRetention = {
auto_anonymize: boolean;
auto_anonymize_days: number | null;
};
1 change: 1 addition & 0 deletions src/server/hapi/createDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ export const createDataset: () => Dataset = () => ({
},
},
},
data_retention: { auto_anonymize: true, auto_anonymize_days: 365 },
affiliate_id: 0,
is_maintenance_mode: false,
is_active: true,
Expand Down
19 changes: 18 additions & 1 deletion src/static/translations/store-form/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,23 @@
}
}
},
"data-retention": {
"label": "Data retention",
"helper_text": "",
"auto-anonymize": {
"label": "Auto-anonymize inactive customers",
"helper_text": "When enabled, customers with no activity for the configured number of days are automatically and irreversibly anonymized.",
"checked": "Yes",
"unchecked": "No"
},
"auto-anonymize-days": {
"label": "Days of inactivity",
"placeholder": "365",
"helper_text": "Minimum 90 days. Customers inactive this long will be anonymized.",
"v8n_required": "Please enter the number of inactive days (at least 90).",
"v8n_too_small": "Please enter at least 90 days."
}
},
"hcaptcha": {
"disclaimer": "We use hCaptcha to protect this page from spam and abuse.",
"privacy_policy": "Privacy Policy",
Expand Down Expand Up @@ -469,4 +486,4 @@
"create": {
"caption": "Create"
}
}
}
Loading