Skip to content

Commit c0b644b

Browse files
committed
ready to get feedback from skin group
1 parent b3add9f commit c0b644b

File tree

3 files changed

+200
-40
lines changed

3 files changed

+200
-40
lines changed

components/SGCMetadataForm.vue

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
v-model="formData.name"
88
id="uploadSetName"
99
type="text"
10-
v-tooltip.top="'A unique name to identify cohort'"
10+
v-tooltip="{value: 'A unique name to identify cohort', position: 'top'}"
1111
:disabled="disabled"
1212
/>
1313
</div>
@@ -17,7 +17,7 @@
1717
v-model="formData.total_sample_size"
1818
id="totalSampleSize"
1919
:min="0"
20-
v-tooltip.top="'Total number of individuals in the study'"
20+
v-tooltip="{value: 'Total number of individuals in the study', position: 'top'}"
2121
:disabled="disabled"
2222
/>
2323
</div>
@@ -30,7 +30,7 @@
3030
v-model="formData.number_of_males"
3131
id="numberOfMales"
3232
:min="0"
33-
v-tooltip.top="'Total number of male participants'"
33+
v-tooltip="{value: 'Total number of male participants', position: 'top'}"
3434
:disabled="disabled"
3535
/>
3636
</div>
@@ -40,7 +40,7 @@
4040
v-model="formData.number_of_females"
4141
id="numberOfFemales"
4242
:min="0"
43-
v-tooltip.top="'Total number of female participants'"
43+
v-tooltip="{value: 'Total number of female participants', position: 'top'}"
4444
:disabled="disabled"
4545
/>
4646
</div>
@@ -53,7 +53,7 @@
5353
v-model="formData.phenotype_coding_system"
5454
id="phenotypeCodingSystem"
5555
type="text"
56-
v-tooltip.top="'The coding system used for phenotype data (e.g., ICD-10, SNOMED CT, etc.)'"
56+
v-tooltip="{value: 'The coding system used for phenotype data (e.g., ICD-10, SNOMED CT, etc.)', position: 'top'}"
5757
:disabled="disabled"
5858
/>
5959
</div>
@@ -78,7 +78,7 @@
7878
v-model="formData.phenotype_mapping_issues"
7979
id="phenotypeMappingIssues"
8080
rows="2"
81-
v-tooltip.top="'Describe any issues encountered during phenotype mapping or enter None if no issues'"
81+
v-tooltip="{value: 'Describe any issues encountered during phenotype mapping or enter None if no issues', position: 'top'}"
8282
:disabled="disabled"
8383
/>
8484
</div>
@@ -88,7 +88,7 @@
8888
v-model="formData.industry_involvement"
8989
id="industryInvolvement"
9090
rows="2"
91-
v-tooltip.top="'Describe any industry partnerships or involvement, or enter None if no industry involvement'"
91+
v-tooltip="{value: 'Describe any industry partnerships or involvement, or enter None if no industry involvement', position: 'top'}"
9292
:disabled="disabled"
9393
/>
9494
</div>
@@ -100,7 +100,7 @@
100100
v-model="formData.data_restrictions"
101101
id="dataRestrictions"
102102
rows="2"
103-
v-tooltip.top="'Describe any restrictions on public data availability, or enter None if no restrictions'"
103+
v-tooltip="{value: 'Describe any restrictions on public data availability, or enter None if no restrictions', position: 'top'}"
104104
:disabled="disabled"
105105
/>
106106
</div>
@@ -132,6 +132,7 @@
132132
<script setup>
133133
import { useToast } from "primevue/usetoast";
134134
import { useDatasetStore } from "~/stores/DatasetStore";
135+
import { nextTick } from 'vue';
135136
136137
// Props
137138
const props = defineProps({
@@ -199,9 +200,26 @@ const metadataSaved = ref(false);
199200
const originalData = ref({ ...props.initialData });
200201
201202
// Watch for prop changes
202-
watch(() => props.initialData, (newData) => {
203-
formData.value = { ...newData };
204-
originalData.value = { ...newData };
203+
watch(() => props.initialData, (newData, oldData) => {
204+
console.log('SGCMetadataForm: Props changed, updating form data:', newData);
205+
console.log('SGCMetadataForm: Old data was:', oldData);
206+
207+
// Only update if the data has actually changed
208+
if (JSON.stringify(newData) !== JSON.stringify(oldData)) {
209+
// Use nextTick to ensure DOM updates properly
210+
nextTick(() => {
211+
// Update each property individually to avoid losing component state
212+
Object.keys(newData).forEach(key => {
213+
if (formData.value[key] !== newData[key]) {
214+
console.log(`Updating ${key}: ${formData.value[key]} -> ${newData[key]}`);
215+
formData.value[key] = newData[key];
216+
}
217+
});
218+
219+
// Update original data
220+
originalData.value = { ...newData };
221+
});
222+
}
205223
}, { deep: true });
206224
207225
// Computed

pages/sgc/edit/[id].vue

Lines changed: 144 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,33 @@
122122
</div>
123123

124124
<!-- Overall Status -->
125-
<div v-if="allTasksCompleted" class="text-center mt-3 p-3" style="background-color: var(--green-100); border: 1px solid var(--green-300); border-radius: 6px;">
126-
<i class="pi pi-check-circle text-green-600 mr-2" style="font-size: 1.5rem"></i>
127-
<span class="text-green-700 font-medium text-lg">Cohort setup complete! All metadata and files uploaded.</span>
125+
<div v-if="allTasksCompleted && !validationPassed" class="text-center mt-3 p-3" style="background-color: var(--blue-50); border: 1px solid var(--blue-200); border-radius: 6px;">
126+
<i class="pi pi-info-circle text-blue-600 mr-2" style="font-size: 1.5rem"></i>
127+
<div class="mb-3">
128+
<span class="text-blue-800 font-medium text-lg">All files uploaded successfully!</span>
129+
<p class="text-blue-700 text-sm mt-2 mb-0">Ready to run inter-file validations to finalize your cohort upload.</p>
130+
</div>
128131

129-
<div class="mt-3">
132+
<div>
130133
<Button
131-
label="Finalize Cohort Upload"
132-
icon="pi pi-check"
133-
class="p-button-success"
134+
label="Run Inter-File Validations"
135+
icon="pi pi-cog"
136+
class="p-button-info"
134137
@click="validateAllConsistency"
135138
:loading="validatingConsistency"
136139
/>
137140
</div>
138141
</div>
139-
<div v-else class="text-center mt-3 p-2" style="background-color: var(--orange-100); border: 1px solid var(--orange-300); border-radius: 6px;">
142+
143+
<!-- Validation Complete Status -->
144+
<div v-if="validationPassed" class="text-center mt-3 p-3" style="background-color: var(--green-50); border: 1px solid var(--green-200); border-radius: 6px;">
145+
<i class="pi pi-check-circle text-green-600 mr-2" style="font-size: 1.5rem"></i>
146+
<div>
147+
<span class="text-green-800 font-medium text-lg">Cohort upload complete!</span>
148+
<p class="text-green-700 text-sm mt-2 mb-0">All files uploaded and validated successfully. Your cohort is now finalized.</p>
149+
</div>
150+
</div>
151+
<div v-else-if="!validationPassed && remainingTasksCount > 0" class="text-center mt-3 p-2" style="background-color: var(--orange-100); border: 1px solid var(--orange-300); border-radius: 6px;">
140152
<i class="pi pi-info-circle text-orange-600 mr-2"></i>
141153
<span class="text-orange-700 font-medium">{{ remainingTasksCount }} task{{ remainingTasksCount === 1 ? '' : 's' }} remaining</span>
142154
</div>
@@ -872,7 +884,7 @@
872884
<Dialog
873885
v-model:visible="store.showNotification"
874886
modal
875-
header="Upload Error"
887+
header="Error"
876888
:style="{ width: '600px' }"
877889
:closable="true"
878890
@hide="store.showNotification = false"
@@ -932,6 +944,7 @@ const loading = ref(true);
932944
const cohortData = ref(null);
933945
const metadataSaved = ref(false);
934946
const validatingConsistency = ref(false);
947+
const validationPassed = ref(false);
935948
936949
// File upload reactive variables
937950
const activeAccordionIndex = ref(0);
@@ -1304,11 +1317,11 @@ onMounted(async () => {
13041317
existingFiles.value.casesControlsFemale = fileInfo;
13051318
} else if (row.file_type === 'cases_controls_both') {
13061319
existingFiles.value.casesControlsBoth = fileInfo;
1307-
} else if (row.file_type === 'cooccurrenceMale') {
1320+
} else if (row.file_type === 'cooccurrence_male') {
13081321
existingFiles.value.cooccurrenceMale = fileInfo;
1309-
} else if (row.file_type === 'cooccurrenceFemale') {
1322+
} else if (row.file_type === 'cooccurrence_female') {
13101323
existingFiles.value.cooccurrenceFemale = fileInfo;
1311-
} else if (row.file_type === 'cooccurrenceBoth') {
1324+
} else if (row.file_type === 'cooccurrence_both') {
13121325
existingFiles.value.cooccurrenceBoth = fileInfo;
13131326
} else if (row.file_type === 'cohort_description') {
13141327
existingFiles.value.cohortDescription = fileInfo;
@@ -1321,11 +1334,17 @@ onMounted(async () => {
13211334
casesControlsMale: uploadedFileTypes.has('cases_controls_male'),
13221335
casesControlsFemale: uploadedFileTypes.has('cases_controls_female'),
13231336
casesControlsBoth: uploadedFileTypes.has('cases_controls_both'),
1324-
cooccurrenceMale: uploadedFileTypes.has('cooccurrenceMale'),
1325-
cooccurrenceFemale: uploadedFileTypes.has('cooccurrenceFemale'),
1326-
cooccurrenceBoth: uploadedFileTypes.has('cooccurrenceBoth'),
1337+
cooccurrenceMale: uploadedFileTypes.has('cooccurrence_male'),
1338+
cooccurrenceFemale: uploadedFileTypes.has('cooccurrence_female'),
1339+
cooccurrenceBoth: uploadedFileTypes.has('cooccurrence_both'),
13271340
cohortDescription: uploadedFileTypes.has('cohort_description')
13281341
};
1342+
1343+
// Initialize validation status from cohort data
1344+
console.log('cohortInfo.validation_status:', cohortInfo.validation_status);
1345+
console.log('Type of validation_status:', typeof cohortInfo.validation_status);
1346+
validationPassed.value = !!cohortInfo.validation_status;
1347+
console.log('Set validationPassed to:', validationPassed.value);
13291348
13301349
// Set initial accordion tab based on completion status
13311350
// If everything is complete, leave all tabs closed (activeAccordionIndex = null)
@@ -1376,15 +1395,46 @@ onMounted(async () => {
13761395
// Handle metadata update
13771396
function handleMetadataUpdated(response) {
13781397
console.log('Metadata updated:', response);
1379-
// Update cohort data with all fields from the response
1380-
cohortData.value = {
1398+
console.log('Current cohortData before update:', cohortData.value);
1399+
1400+
// Handle the response - it could be an array (like fetch) or an object
1401+
const cohortInfo = Array.isArray(response) ? response[0] : response;
1402+
1403+
// Create the updated data object
1404+
const updatedData = {
13811405
...cohortData.value,
1382-
id: response.cohort_id || response.id, // Handle both field names
1383-
name: response.name,
1384-
total_sample_size: response.total_sample_size,
1385-
number_of_males: response.number_of_males,
1386-
number_of_females: response.number_of_females
1406+
id: cohortInfo.cohort_id || cohortInfo.id, // Handle both field names
1407+
name: cohortInfo.name,
1408+
total_sample_size: cohortInfo.total_sample_size,
1409+
number_of_males: cohortInfo.number_of_males,
1410+
number_of_females: cohortInfo.number_of_females,
1411+
uploaded_by: cohortInfo.uploaded_by,
1412+
created_at: cohortInfo.created_at,
1413+
updated_at: cohortInfo.updated_at,
1414+
// Extract cohort metadata fields if they exist
1415+
...(cohortInfo.cohort_metadata ? {
1416+
phenotype_coding_system: cohortInfo.cohort_metadata.phenotype_coding_system || '',
1417+
phenotype_mapping_issues: cohortInfo.cohort_metadata.phenotype_mapping_issues || '',
1418+
industry_involvement: cohortInfo.cohort_metadata.industry_involvement || '',
1419+
industry_authorship: cohortInfo.cohort_metadata.industry_authorship,
1420+
data_restrictions: cohortInfo.cohort_metadata.data_restrictions || ''
1421+
} : {
1422+
phenotype_coding_system: cohortData.value.phenotype_coding_system || '',
1423+
phenotype_mapping_issues: cohortData.value.phenotype_mapping_issues || '',
1424+
industry_involvement: cohortData.value.industry_involvement || '',
1425+
industry_authorship: cohortData.value.industry_authorship,
1426+
data_restrictions: cohortData.value.data_restrictions || ''
1427+
})
13871428
};
1429+
1430+
console.log('Updated cohortData after processing:', updatedData);
1431+
1432+
// Update cohort data
1433+
cohortData.value = updatedData;
1434+
1435+
// Reset validation status since metadata has changed
1436+
validationPassed.value = false;
1437+
13881438
// Show the file upload accordion
13891439
metadataSaved.value = true;
13901440
}
@@ -1626,7 +1676,7 @@ async function uploadCooccurrenceFile(gender = 'male') {
16261676
const result = await store.uploadSGCFile(
16271677
fileRef.value,
16281678
cohortId,
1629-
'cooccurrence',
1679+
`cooccurrence_${gender}`,
16301680
statusKey,
16311681
createFlippedMapping(mappingRef)
16321682
);
@@ -1947,6 +1997,77 @@ function openNextAccordion() {
19471997
}
19481998
}, 500);
19491999
}
2000+
2001+
// Function to validate all file consistency
2002+
async function validateAllConsistency() {
2003+
validatingConsistency.value = true;
2004+
2005+
// Clear any previous error state to prevent interceptor interference
2006+
store.errorMessage = '';
2007+
store.showNotification = false;
2008+
2009+
try {
2010+
const result = await store.validateAllConsistency(cohortId);
2011+
2012+
// Check if validation passed based on the API response
2013+
if (result && result.validation_status) {
2014+
// Update validation status to hide UI elements
2015+
validationPassed.value = true;
2016+
2017+
// Show success message using toast for positive feedback
2018+
toast.add({
2019+
severity: 'success',
2020+
summary: 'Validation Complete',
2021+
detail: 'All inter-file validations passed successfully! Your cohort is now finalized.',
2022+
life: 5000
2023+
});
2024+
2025+
// Optionally redirect to the cohort list after showing success
2026+
setTimeout(() => {
2027+
navigateTo('/sgc');
2028+
}, 3000);
2029+
} else {
2030+
// Handle case where API returns 200 but validation_status is false
2031+
toast.add({
2032+
severity: 'error',
2033+
summary: 'Validation Failed',
2034+
detail: result?.message || 'Inter-file validation failed. Please check your files and try again.',
2035+
life: 8000
2036+
});
2037+
}
2038+
2039+
} catch (error) {
2040+
console.error('Validation error:', error);
2041+
store.processing = false;
2042+
2043+
// Handle server validation errors (400 responses) - exact same pattern as cohort description upload
2044+
let errorMessage = 'Inter-file validation failed. Please check your files and try again.';
2045+
if (error.status === 400 || error.response?.status === 400) {
2046+
// Server returned validation errors - check both error.data and error.response.data
2047+
const errorData = error.data || error.response?.data;
2048+
2049+
if (typeof errorData === 'string') {
2050+
errorMessage = errorData;
2051+
} else if (errorData?.detail) {
2052+
errorMessage = errorData.detail;
2053+
} else if (errorData?.message) {
2054+
errorMessage = errorData.message;
2055+
}
2056+
} else if (error.message) {
2057+
errorMessage = error.message;
2058+
}
2059+
2060+
toast.add({
2061+
severity: 'error',
2062+
summary: 'Validation Error',
2063+
detail: errorMessage,
2064+
life: 8000 // Longer display time for validation errors
2065+
});
2066+
2067+
} finally {
2068+
validatingConsistency.value = false;
2069+
}
2070+
}
19502071
</script>
19512072
19522073
<style scoped>

0 commit comments

Comments
 (0)