Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import './index';

import { Summary } from '../../../storygen/Summary';
import { getMeta } from '../../../storygen/getMeta';
import { getStory } from '../../../storygen/getStory';

const summary: Summary = {
href: 'https://demo.api/hapi/data_retention_settings/0',
parent: 'https://demo.api/hapi/data_retention_settings',
nucleon: true,
localName: 'foxy-data-retention-settings-form',
translatable: true,
configurable: {},
};

export default getMeta(summary);

export const Playground = getStory({ ...summary, code: true });
export const Empty = getStory(summary);
export const Error = getStory(summary);
export const Busy = getStory(summary);

Empty.args.href = '';
Error.args.href = 'https://demo.api/virtual/empty?status=404';
Busy.args.href = 'https://demo.api/virtual/stall';
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import './index';

import { expect, fixture, html } from '@open-wc/testing';
import { InternalForm } from '../../internal/InternalForm/InternalForm';
import { DataRetentionSettingsForm as Form } from './DataRetentionSettingsForm';

describe('DataRetentionSettingsForm', () => {
it('imports and registers foxy-internal-summary-control', () => {
expect(customElements.get('foxy-internal-summary-control')).to.exist;
});

it('imports and registers foxy-internal-switch-control', () => {
expect(customElements.get('foxy-internal-switch-control')).to.exist;
});

it('imports and registers foxy-internal-number-control', () => {
expect(customElements.get('foxy-internal-number-control')).to.exist;
});

it('imports and registers foxy-internal-form', () => {
expect(customElements.get('foxy-internal-form')).to.exist;
});

it('registers as foxy-data-retention-settings-form', () => {
expect(customElements.get('foxy-data-retention-settings-form')).to.equal(Form);
});

it('extends InternalForm', () => {
expect(new Form()).to.be.instanceOf(InternalForm);
});

it('has a default i18n namespace of "data-retention-settings-form"', () => {
expect(Form).to.have.property('defaultNS', 'data-retention-settings-form');
});

describe('v8n', () => {
it('allows an unset auto_anonymize_days', () => {
const element = new Form();
expect(element.errors).to.not.include('auto-anonymize-days:v8n_too_small');
expect(element.errors).to.not.include('auto-anonymize-days:v8n_required');
});

it('produces "auto-anonymize-days:v8n_too_small" when below 90', () => {
const element = new Form();
element.edit({ auto_anonymize_days: 89 });
expect(element.errors).to.include('auto-anonymize-days:v8n_too_small');

element.edit({ auto_anonymize_days: 90 });
expect(element.errors).to.not.include('auto-anonymize-days:v8n_too_small');
});

it('requires days when auto_anonymize is enabled', () => {
const element = new Form();
element.edit({ auto_anonymize: true });
expect(element.errors).to.include('auto-anonymize-days:v8n_required');

element.edit({ auto_anonymize_days: 365 });
expect(element.errors).to.not.include('auto-anonymize-days:v8n_required');
});
});

describe('hiddenSelector', () => {
it('always hides delete and timestamps', async () => {
const element = await fixture<Form>(
html`<foxy-data-retention-settings-form></foxy-data-retention-settings-form>`
);

expect(element.hiddenSelector.matches('delete', true)).to.be.true;
expect(element.hiddenSelector.matches('timestamps', true)).to.be.true;
});

it('hides the days field when auto_anonymize is off', async () => {
const element = await fixture<Form>(
html`<foxy-data-retention-settings-form></foxy-data-retention-settings-form>`
);

element.edit({ auto_anonymize: false });
expect(element.hiddenSelector.matches('general:auto-anonymize-days', true)).to.be.true;
});

it('shows the days field when auto_anonymize is on', async () => {
const element = await fixture<Form>(
html`<foxy-data-retention-settings-form></foxy-data-retention-settings-form>`
);

element.edit({ auto_anonymize: true });
expect(element.hiddenSelector.matches('general:auto-anonymize-days', true)).to.be.false;
});
});

it('renders a switch control for auto_anonymize', async () => {
const element = await fixture<Form>(
html`<foxy-data-retention-settings-form></foxy-data-retention-settings-form>`
);

const control = element.renderRoot.querySelector(
'foxy-internal-switch-control[infer="auto-anonymize"]'
);

expect(control).to.exist;
});

it('renders a number control for auto_anonymize_days when enabled', async () => {
const element = await fixture<Form>(
html`<foxy-data-retention-settings-form></foxy-data-retention-settings-form>`
);

element.edit({ auto_anonymize: true });
await element.requestUpdate();

const control = element.renderRoot.querySelector(
'foxy-internal-number-control[infer="auto-anonymize-days"]'
);

expect(control).to.exist;
expect(control).to.have.attribute('min', '90');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { TemplateResult } from 'lit-html';
import type { NucleonV8N } from '../NucleonElement/types';
import type { Data } from './types';

import { TranslatableMixin } from '../../../mixins/translatable';
import { BooleanSelector } from '@foxy.io/sdk/core';
import { InternalForm } from '../../internal/InternalForm/InternalForm';
import { html } from 'lit-html';

const NS = 'data-retention-settings-form';
const Base = TranslatableMixin(InternalForm, NS);

/**
* Form element for editing `fx:data_retention_settings` resources.
*
* Per-store auto-anonymization config: a toggle and a "days of inactivity"
* value with a hard 90-day minimum (enforced client-side and by the API).
*
* @element foxy-data-retention-settings-form
* @since 1.52.0
*/
export class DataRetentionSettingsForm extends Base<Data> {
static get v8n(): NucleonV8N<Data> {
return [
({ auto_anonymize_days: v }) => {
return (
v === null ||
v === undefined ||
(Number.isInteger(v) && v >= 90) ||
'auto-anonymize-days:v8n_too_small'
);
},
({ auto_anonymize: enabled, auto_anonymize_days: v }) => {
return !enabled || (typeof v === 'number' && v >= 90) || 'auto-anonymize-days:v8n_required';
},
];
}

get hiddenSelector(): BooleanSelector {
// No DELETE route for this resource, and it carries no timestamps.
const alwaysMatch = ['delete', 'timestamps', super.hiddenSelector.toString()];
// The "days" field only applies when auto-anonymization is on.
if (!this.form.auto_anonymize) alwaysMatch.unshift('general:auto-anonymize-days');
return new BooleanSelector(alwaysMatch.join(' ').trim());
}

renderBody(): TemplateResult {
return html`
<foxy-internal-summary-control infer="general">
<foxy-internal-switch-control infer="auto-anonymize" layout="summary-item">
</foxy-internal-switch-control>

<foxy-internal-number-control
layout="summary-item"
infer="auto-anonymize-days"
min="90"
step="1"
>
</foxy-internal-number-control>
</foxy-internal-summary-control>

${super.renderBody()}
`;
}
}
10 changes: 10 additions & 0 deletions src/elements/public/DataRetentionSettingsForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '../../internal/InternalSummaryControl/index';
import '../../internal/InternalSwitchControl/index';
import '../../internal/InternalNumberControl/index';
import '../../internal/InternalForm/index';

import { DataRetentionSettingsForm } from './DataRetentionSettingsForm';

customElements.define('foxy-data-retention-settings-form', DataRetentionSettingsForm);

export { DataRetentionSettingsForm };
4 changes: 4 additions & 0 deletions src/elements/public/DataRetentionSettingsForm/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Resource } from '@foxy.io/sdk/core';
import type { Rels } from '@foxy.io/sdk/backend';

export type Data = Resource<Rels.DataRetentionSettings>;
1 change: 1 addition & 0 deletions src/elements/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export { CustomerPortalSettingsForm } from './CustomerPortalSettingsForm/Custome
export { CustomersTable } from './CustomersTable/CustomersTable';
export { CustomFieldCard } from './CustomFieldCard/CustomFieldCard';
export { CustomFieldForm } from './CustomFieldForm/CustomFieldForm';
export { DataRetentionSettingsForm } from './DataRetentionSettingsForm/DataRetentionSettingsForm';
export { DiscountBuilder } from './DiscountBuilder/DiscountBuilder';
export { DiscountCard } from './DiscountCard/DiscountCard';
export { DiscountDetailCard } from './DiscountDetailCard/DiscountDetailCard';
Expand Down
9 changes: 9 additions & 0 deletions src/server/hapi/createDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,15 @@ export const createDataset: () => Dataset = () => ({
},
],

data_retention_settings: [
{
id: 0,
store_id: 0,
auto_anonymize: true,
auto_anonymize_days: 365,
},
],

customer_portal_settings: [
{
id: 0,
Expand Down
4 changes: 4 additions & 0 deletions src/server/hapi/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ export const links: Links = {
'fx:store': { href: `./stores/${store_id}` },
}),

data_retention_settings: ({ store_id }) => ({
'fx:store': { href: `./stores/${store_id}` },
}),

taxes: ({ id, store_id }) => ({
'fx:store': { href: `./stores/${store_id}` },
'fx:tax_item_categories': { href: `./tax_item_categories?tax_id=${id}` },
Expand Down
52 changes: 52 additions & 0 deletions src/static/translations/data-retention-settings-form/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"header": {
"title_existing": "Data retention",
"title_new": "Data retention",
"subtitle": "Automatic anonymization of inactive customers",
"copy-id": {
"failed_to_copy": "Failed to copy",
"click_to_copy": "Copy ID",
"copying": "Copying...",
"done": "Copied to clipboard"
},
"copy-json": {
"failed_to_copy": "Failed to copy",
"click_to_copy": "Copy source as JSON",
"copying": "Copying...",
"done": "Copied to clipboard"
}
},
"general": {
"label": "",
"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."
},
"auto-anonymize-days": {
"label": "Days of inactivity",
"placeholder": "365",
"helper_text": "Minimum 90 days. Customers inactive this long will be anonymized.",
"v8n_required": "Enter the number of inactive days (at least 90).",
"v8n_too_small": "Must be at least 90 days."
}
},
"timestamps": {
"date_created": "Created on",
"date_modified": "Last updated on",
"date": "{{value, date}}"
},
"delete": {
"delete": "Delete",
"cancel": "Cancel",
"delete_prompt": "Are you sure you'd like to reset these settings?"
},
"undo": { "caption": "Undo" },
"submit": { "caption": "Save changes" },
"create": { "caption": "Create" },
"spinner": {
"refresh": "Refresh",
"loading_busy": "Loading",
"loading_error": "Unknown error"
}
}