chore: update DPO contact information across legal pages
- Replace specific DPO name with generic privacy email across all legal templates - Update DPA to clarify third-party services vs sub-processors distinction - Add privacy policy and DPA cross-references in Terms - Add intellectual property section to Terms - Improve prompts UI with Yes/No buttons, section headers, and better visual hierarchy Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
35e9e2a84b
commit
37b7602027
|
|
@ -119,8 +119,8 @@
|
|||
<h3>Data Processor.</h3>
|
||||
<p><span class="inou-brand">inou</span>. We store, encrypt, and transmit your data according to your instructions.</p>
|
||||
|
||||
<h3>Sub-processors.</h3>
|
||||
<p>Third-party services you explicitly connect to your account, such as AI assistants. We do not use sub-processors for storage or core functionality.</p>
|
||||
<h3>Third-party services.</h3>
|
||||
<p>You may connect external services to your account, such as AI assistants. These services operate as independent controllers or as processors engaged directly by you — not as our sub-processors. We do not engage sub-processors for storage or core functionality.</p>
|
||||
</div>
|
||||
|
||||
<div class="dpa-card">
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
|
||||
<div class="dpa-card">
|
||||
<h2>Contact</h2>
|
||||
<p>Data Protection Officer: Johan Jongsma</p>
|
||||
<p>Data Protection Officer: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
|
||||
<p>Questions about data processing: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
|
||||
<p>This agreement was last updated on February 8, 2026.</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@
|
|||
<p>We comply with <strong>FADP</strong> (Swiss data protection), <strong>GDPR</strong> (European data protection), and <strong>HIPAA</strong> (US medical privacy) standards. Regardless of where you live, you get our highest level of protection.</p>
|
||||
<p>We may update this policy. Registered users will be notified by email of material changes. Continued use after changes constitutes acceptance.</p>
|
||||
<p>Regardless of your jurisdiction, you may request access to your data, correction of inaccuracies, or complete deletion of your account. We will respond within 30 days.</p>
|
||||
<p>Our Data Protection Officer is Johan Jongsma. For all privacy and data protection inquiries, contact <a href="mailto:privacy@inou.com">privacy@inou.com</a>.</p>
|
||||
<p>Data Protection Officer: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
|
||||
<p>This policy was last updated on February 8, 2026.</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
|
||||
<div class="prompt-list">
|
||||
{{/* 1. FREEFORM CARD - Always visible */}}
|
||||
<div style="padding: 12px 20px; background: #f8fafc; border-bottom: 1px solid var(--border);">
|
||||
<span style="font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b;">Add New Entry</span>
|
||||
</div>
|
||||
{{range .DuePrompts}}
|
||||
{{if .IsFreeform}}
|
||||
<div class="prompt-item prompt-freeform" data-prompt-id="{{.ID}}">
|
||||
|
|
@ -49,6 +52,9 @@
|
|||
{{end}}
|
||||
|
||||
{{/* 2. PENDING CARDS - Due but not filled yet */}}
|
||||
<div style="padding: 12px 20px; background: #f8fafc; border-bottom: 1px solid var(--border); margin-top: 24px;">
|
||||
<span style="font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b;">Due Now</span>
|
||||
</div>
|
||||
{{range .DuePrompts}}
|
||||
{{if not .IsFreeform}}
|
||||
{{if not .HasResponse}}
|
||||
|
|
@ -81,11 +87,11 @@
|
|||
<button type="button" class="btn-save" onclick="saveItem(this.closest('.prompt-item'))">Save</button>
|
||||
</div>
|
||||
{{else if eq .Type "checkbox"}}
|
||||
<label class="prompt-checkbox">
|
||||
<input type="checkbox" name="field_{{.Key}}" value="1">
|
||||
<span class="prompt-checkbox-box"></span>
|
||||
<span class="prompt-checkbox-label">{{if .Label}}{{.Label}}{{else}}Yes{{end}}</span>
|
||||
</label>
|
||||
<div class="prompt-buttons">
|
||||
<button type="button" class="prompt-btn" data-field="{{.Key}}" data-value="yes" onclick="selectYesNo(this)">Yes</button>
|
||||
<button type="button" class="prompt-btn" data-field="{{.Key}}" data-value="no" onclick="selectYesNo(this)">No</button>
|
||||
<input type="hidden" name="field_{{.Key}}" value="">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
@ -98,6 +104,11 @@
|
|||
{{end}}
|
||||
|
||||
{{/* 3. FILLED CARDS - Entries from today */}}
|
||||
{{if .Entries}}
|
||||
<div style="padding: 12px 20px; background: #f8fafc; border-bottom: 1px solid var(--border); margin-top: 24px;">
|
||||
<span style="font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b;">Completed Today</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{range .Entries}}
|
||||
<div class="prompt-item prompt-filled" data-entry-id="{{.ID}}">
|
||||
<a href="#" class="prompt-dismiss" onclick="deleteEntry('{{.ID}}'); return false;" title="Delete">✕</a>
|
||||
|
|
@ -430,6 +441,35 @@
|
|||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.prompt-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.prompt-btn {
|
||||
padding: 10px 24px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
min-width: 80px;
|
||||
}
|
||||
.prompt-btn:hover {
|
||||
border-color: var(--accent);
|
||||
background: #fff8f6;
|
||||
}
|
||||
.prompt-btn.selected {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
.prompt-btn:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(198, 93, 7, 0.2);
|
||||
}
|
||||
.prompt-scale {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
|
@ -521,24 +561,43 @@
|
|||
font-style: italic;
|
||||
}
|
||||
.prompt-freeform {
|
||||
background: #fefce8;
|
||||
border-left: 4px solid #eab308;
|
||||
background: #fff;
|
||||
border-left: 3px solid #94a3b8;
|
||||
}
|
||||
.prompt-pending {
|
||||
background: #fff;
|
||||
border-left: 4px solid var(--accent);
|
||||
border-left: 3px solid var(--accent);
|
||||
}
|
||||
.prompt-filled {
|
||||
background: #f0fdf4;
|
||||
border-left: 4px solid #16a34a;
|
||||
background: #fafafa;
|
||||
border-left: 3px solid #64748b;
|
||||
}
|
||||
.prompt-preview {
|
||||
opacity: 0.6;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.prompt-preview input[disabled] {
|
||||
cursor: not-allowed;
|
||||
background: #f9fafb;
|
||||
}
|
||||
.prompt-question {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
.prompt-category {
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #64748b;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.prompt-due {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
.entry-readonly .prompt-field-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -653,6 +712,10 @@ document.querySelectorAll('.prompt-item').forEach(item => {
|
|||
cb.addEventListener('change', () => saveItem(item));
|
||||
});
|
||||
|
||||
item.querySelectorAll('.prompt-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => selectYesNo(btn));
|
||||
});
|
||||
|
||||
item.querySelectorAll('.prompt-scale-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const field = btn.dataset.field;
|
||||
|
|
@ -723,6 +786,25 @@ function editPrompt(btn) {
|
|||
item.querySelector('.prompt-form input:not([type=hidden]), .prompt-form textarea')?.focus();
|
||||
}
|
||||
|
||||
function selectYesNo(btn) {
|
||||
const item = btn.closest('.prompt-item');
|
||||
const field = btn.dataset.field;
|
||||
const value = btn.dataset.value;
|
||||
|
||||
// Deselect siblings
|
||||
btn.parentElement.querySelectorAll('.prompt-btn').forEach(b => b.classList.remove('selected'));
|
||||
|
||||
// Select this one
|
||||
btn.classList.add('selected');
|
||||
|
||||
// Update hidden input
|
||||
const hidden = item.querySelector('input[name="field_' + field + '"]');
|
||||
if (hidden) hidden.value = value;
|
||||
|
||||
// Auto-save
|
||||
saveItem(item);
|
||||
}
|
||||
|
||||
function addNewPromptCard(prompt) {
|
||||
const container = document.querySelector('.prompts-section');
|
||||
if (!container) return;
|
||||
|
|
@ -737,10 +819,11 @@ function addNewPromptCard(prompt) {
|
|||
prompt.input_config.fields.forEach(field => {
|
||||
if (field.type === 'checkbox') {
|
||||
fieldsHtml += `
|
||||
<label class="prompt-checkbox">
|
||||
<input type="checkbox" name="field_${field.key}" value="1">
|
||||
<span class="prompt-checkbox-box"></span>
|
||||
</label>`;
|
||||
<div class="prompt-buttons">
|
||||
<button type="button" class="prompt-btn" data-field="${field.key}" data-value="yes" onclick="selectYesNo(this)">Yes</button>
|
||||
<button type="button" class="prompt-btn" data-field="${field.key}" data-value="no" onclick="selectYesNo(this)">No</button>
|
||||
<input type="hidden" name="field_${field.key}" value="">
|
||||
</div>`;
|
||||
} else if (field.type === 'number') {
|
||||
const step = field.step || (field.datatype === 'float' ? '0.1' : '1');
|
||||
const min = field.min !== undefined ? `min="${field.min}"` : '';
|
||||
|
|
@ -820,7 +903,7 @@ function addNewPromptCard(prompt) {
|
|||
async function saveItem(item) {
|
||||
const form = item.querySelector('.prompt-form');
|
||||
const promptId = form.dataset.promptId;
|
||||
const inputs = form.querySelectorAll('input:not([type=hidden]), textarea');
|
||||
const inputs = form.querySelectorAll('input, textarea');
|
||||
|
||||
const response = {};
|
||||
let hasValue = false;
|
||||
|
|
@ -828,6 +911,8 @@ async function saveItem(item) {
|
|||
let responseRaw = '';
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (!input.name) return; // Skip inputs without names
|
||||
|
||||
const key = input.name.replace('field_', '').replace('response_raw', 'raw');
|
||||
if (input.type === 'checkbox') {
|
||||
if (input.checked) {
|
||||
|
|
@ -836,7 +921,13 @@ async function saveItem(item) {
|
|||
displayValue = '✓';
|
||||
responseRaw = 'yes';
|
||||
}
|
||||
} else if (input.value) {
|
||||
} else if (input.type === 'hidden' && input.value && input.name.startsWith('field_')) {
|
||||
// Handle hidden inputs from Yes/No buttons
|
||||
response[key] = input.value;
|
||||
hasValue = true;
|
||||
displayValue = input.value === 'yes' ? '✓' : '✗';
|
||||
responseRaw = input.value;
|
||||
} else if (input.value && input.type !== 'hidden') {
|
||||
response[key] = input.value;
|
||||
hasValue = true;
|
||||
displayValue += (displayValue ? ' / ' : '') + input.value;
|
||||
|
|
@ -974,11 +1065,11 @@ function addPendingCard(prompt) {
|
|||
</div>`;
|
||||
} else if (field.type === 'checkbox') {
|
||||
fieldsHtml = `
|
||||
<label class="prompt-checkbox">
|
||||
<input type="checkbox" name="field_${field.key}" value="1">
|
||||
<span class="prompt-checkbox-box"></span>
|
||||
<span class="prompt-checkbox-label">Yes</span>
|
||||
</label>`;
|
||||
<div class="prompt-buttons">
|
||||
<button type="button" class="prompt-btn" data-field="${field.key}" data-value="yes" onclick="selectYesNo(this)">Yes</button>
|
||||
<button type="button" class="prompt-btn" data-field="${field.key}" data-value="no" onclick="selectYesNo(this)">No</button>
|
||||
<input type="hidden" name="field_${field.key}" value="">
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,12 +121,17 @@
|
|||
<h2>Our responsibilities</h2>
|
||||
|
||||
<h3>What we provide.</h3>
|
||||
<p>We will store your data securely using FIPS 140-3 validated encryption, make it available to you through the platform, and transmit it to third-party services you explicitly authorize. We will notify you of material changes to these terms or our privacy practices.</p>
|
||||
<p>We will store your data securely using FIPS 140-3 validated encryption, make it available to you through the platform, and transmit it to third-party services you explicitly authorize. For details on how we protect your data, see our <a href="/security">security page</a>. We will notify you of material changes to these terms or our privacy practices.</p>
|
||||
|
||||
<h3>What we don't guarantee.</h3>
|
||||
<p>We aim for continuous availability but cannot guarantee it. The service may be temporarily unavailable for maintenance, updates, or circumstances beyond our control. We are not liable for decisions you or others make based on data viewed through the platform.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-card">
|
||||
<h2>Privacy and data processing</h2>
|
||||
<p>Your use of <span class="inou-brand">inou</span> is subject to our <a href="/privacy-policy">Privacy Policy</a> and <a href="/legal/dpa">Data Processing Agreement</a>, which describe what data we collect, how we use it, and your rights. By using the service, you acknowledge and agree to those terms.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-card">
|
||||
<h2>Acceptable use</h2>
|
||||
|
||||
|
|
@ -137,6 +142,12 @@
|
|||
<p>We may suspend or terminate your account. In cases of illegal activity, we will cooperate with law enforcement.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-card">
|
||||
<h2>Intellectual property</h2>
|
||||
<p>Your data is yours. You retain all rights to the health data, files, and information you upload. We claim no ownership over your content.</p>
|
||||
<p>The <span class="inou-brand">inou</span> platform — its software, design, branding, and documentation — is our property. These terms grant you a personal, non-exclusive, non-transferable license to use the service. They do not grant you rights to our code, design, or brand.</p>
|
||||
</div>
|
||||
|
||||
<div class="terms-card">
|
||||
<h2>Payment</h2>
|
||||
|
||||
|
|
@ -175,7 +186,7 @@
|
|||
<div class="terms-card">
|
||||
<h2>Changes</h2>
|
||||
<p>We may update these terms. Registered users will be notified by email of material changes. Continued use after changes constitutes acceptance.</p>
|
||||
<p>Questions: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
|
||||
<p>Data Protection Officer: <a href="mailto:privacy@inou.com">privacy@inou.com</a></p>
|
||||
<p>Last updated: February 8, 2026.</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue