Compare commits
2 commits
feat/datas
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b04ad9910 | |||
| fbc34a7456 |
8 changed files with 595 additions and 1676 deletions
|
|
@ -619,7 +619,7 @@ export default class DatasetsController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async doiStore({ request, response, session, auth }: HttpContext) {
|
public async doiStore({ request, response, auth }: HttpContext) {
|
||||||
const dataId = request.param('publish_id');
|
const dataId = request.param('publish_id');
|
||||||
const user = auth.user;
|
const user = auth.user;
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
@ -627,7 +627,10 @@ export default class DatasetsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load dataset with minimal required relationships
|
// Load dataset with minimal required relationships
|
||||||
const dataset = await Dataset.query().where('editor_id', user.id).where('publish_id', dataId).firstOrFail();
|
const dataset = await Dataset.query()
|
||||||
|
.where('editor_id', user.id) // Ensure the user is the editor of the dataset
|
||||||
|
.where('publish_id', dataId)
|
||||||
|
.firstOrFail();
|
||||||
|
|
||||||
const prefix = process.env.DATACITE_PREFIX || '';
|
const prefix = process.env.DATACITE_PREFIX || '';
|
||||||
const base_domain = process.env.BASE_DOMAIN || '';
|
const base_domain = process.env.BASE_DOMAIN || '';
|
||||||
|
|
@ -636,10 +639,9 @@ export default class DatasetsController {
|
||||||
const xmlMeta = (await Index.getDoiRegisterString(dataset)) as string;
|
const xmlMeta = (await Index.getDoiRegisterString(dataset)) as string;
|
||||||
|
|
||||||
// Prepare DOI registration data
|
// Prepare DOI registration data
|
||||||
const doiValue = `${prefix}/tethys.${dataset.publish_id}`;
|
const doiValue = `${prefix}/tethys.${dataset.publish_id}`; //'10.21388/tethys.213'
|
||||||
const landingPageUrl = `https://doi.${getDomain(base_domain)}/${prefix}/tethys.${dataset.publish_id}`;
|
const landingPageUrl = `https://doi.${getDomain(base_domain)}/${prefix}/tethys.${dataset.publish_id}`; //https://doi.dev.tethys.at/10.21388/tethys.213
|
||||||
|
|
||||||
try {
|
|
||||||
// Register DOI with DataCite
|
// Register DOI with DataCite
|
||||||
const doiClient = new DoiClient();
|
const doiClient = new DoiClient();
|
||||||
const dataciteResponse = await doiClient.registerDoi(doiValue, xmlMeta, landingPageUrl);
|
const dataciteResponse = await doiClient.registerDoi(doiValue, xmlMeta, landingPageUrl);
|
||||||
|
|
@ -650,30 +652,17 @@ export default class DatasetsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOI registration successful - persist and index
|
// DOI registration successful - persist and index
|
||||||
|
try {
|
||||||
|
// Save identifier
|
||||||
await this.persistDoiAndIndex(dataset, doiValue);
|
await this.persistDoiAndIndex(dataset, doiValue);
|
||||||
|
|
||||||
return response
|
return response.toRoute('editor.dataset.list').flash('message', 'You have successfully created a DOI for the dataset!');
|
||||||
.flash('message', 'You have successfully created a DOI for the dataset!')
|
|
||||||
.redirect()
|
|
||||||
.toRoute('editor.dataset.list');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// logger.error(`${__filename}: DOI registration failed for dataset ${dataset.id}: ${error.message}`);
|
logger.error(`${__filename}: Failed to persist DOI and index dataset ${dataset.id}: ${error.message}`);
|
||||||
|
throw new HttpException(error.message);
|
||||||
if (error instanceof DoiClientException) {
|
|
||||||
// Flash error for Inertia to pick up
|
|
||||||
session.flash('errors', {
|
|
||||||
doi: `DOI registration failed: ${error.message}`,
|
|
||||||
});
|
|
||||||
// Optionally also flash a warning for your warning display
|
|
||||||
session.flash('warning', error.message);
|
|
||||||
} else {
|
|
||||||
session.flash('errors', {
|
|
||||||
general: `An unexpected error occurred: ${error.message}`,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.redirect().back();
|
// return response.toRoute('editor.dataset.list').flash('message', xmlMeta);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -821,10 +810,6 @@ export default class DatasetsController {
|
||||||
referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes).map(([key, value]) => ({ value: key, label: value })),
|
referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||||
relationTypes: Object.entries(RelationTypes).map(([key, value]) => ({ value: key, label: value })),
|
relationTypes: Object.entries(RelationTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||||
doctypes: DatasetTypes,
|
doctypes: DatasetTypes,
|
||||||
can: {
|
|
||||||
edit: await auth.user?.can(['dataset-editor-update']),
|
|
||||||
// delete: await auth.user?.can(['dataset-delete']),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface XslTParameter {
|
||||||
}
|
}
|
||||||
export default {
|
export default {
|
||||||
// opensearchNode: process.env.OPENSEARCH_HOST || 'localhost',
|
// opensearchNode: process.env.OPENSEARCH_HOST || 'localhost',
|
||||||
client: new Client({ node: `${process.env.OPENSEARCH_HOST || 'localhost'}` }), // replace with your OpenSearch endpoint
|
client: new Client({ node: `http://${process.env.OPENSEARCH_HOST || 'localhost'}` }), // replace with your OpenSearch endpoint
|
||||||
|
|
||||||
async getDoiRegisterString(dataset: Dataset): Promise<string | undefined> {
|
async getDoiRegisterString(dataset: Dataset): Promise<string | undefined> {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -62,19 +62,15 @@
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
|
|
||||||
<!--<datacite:creator>-->
|
<!--<datacite:creator>-->
|
||||||
<xsl:if test="PersonAuthor[normalize-space(concat(@FirstName, @LastName)) != '']">
|
|
||||||
<creators>
|
<creators>
|
||||||
<xsl:apply-templates select="PersonAuthor" mode="oai_datacite">
|
<xsl:apply-templates select="PersonAuthor" mode="oai_datacite">
|
||||||
<xsl:sort select="@SortOrder"/>
|
<xsl:sort select="@SortOrder"/>
|
||||||
</xsl:apply-templates>
|
</xsl:apply-templates>
|
||||||
</creators>
|
</creators>
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="TitleMain[normalize-space(@Value) != ''] or TitleAdditional[normalize-space(@Value) != '']">
|
|
||||||
<titles>
|
<titles>
|
||||||
<xsl:apply-templates select="TitleMain" mode="oai_datacite" />
|
<xsl:apply-templates select="TitleMain" mode="oai_datacite" />
|
||||||
<xsl:apply-templates select="TitleAdditional" mode="oai_datacite" />
|
<xsl:apply-templates select="TitleAdditional" mode="oai_datacite" />
|
||||||
</titles>
|
</titles>
|
||||||
</xsl:if>
|
|
||||||
<publisher>
|
<publisher>
|
||||||
<!-- <xsl:value-of select="@PublisherName" /> -->
|
<!-- <xsl:value-of select="@PublisherName" /> -->
|
||||||
<xsl:value-of select="@CreatingCorporation" />
|
<xsl:value-of select="@CreatingCorporation" />
|
||||||
|
|
@ -82,26 +78,22 @@
|
||||||
<publicationYear>
|
<publicationYear>
|
||||||
<xsl:value-of select="ServerDatePublished/@Year" />
|
<xsl:value-of select="ServerDatePublished/@Year" />
|
||||||
</publicationYear>
|
</publicationYear>
|
||||||
<xsl:if test="Subject[normalize-space(@Value) != '']">
|
|
||||||
<subjects>
|
<subjects>
|
||||||
<xsl:apply-templates select="Subject" mode="oai_datacite" />
|
<xsl:apply-templates select="Subject" mode="oai_datacite" />
|
||||||
</subjects>
|
</subjects>
|
||||||
</xsl:if>
|
|
||||||
<language>
|
<language>
|
||||||
<xsl:value-of select="@Language" />
|
<xsl:value-of select="@Language" />
|
||||||
</language>
|
</language>
|
||||||
<xsl:if test="PersonContributor[normalize-space(concat(@FirstName, @LastName)) != '']">
|
<xsl:if test="PersonContributor">
|
||||||
<contributors>
|
<contributors>
|
||||||
<xsl:apply-templates select="PersonContributor" mode="oai_datacite">
|
<xsl:apply-templates select="PersonContributor" mode="oai_datacite">
|
||||||
<xsl:sort select="@SortOrder"/>
|
<xsl:sort select="@SortOrder"/>
|
||||||
</xsl:apply-templates>
|
</xsl:apply-templates>
|
||||||
</contributors>
|
</contributors>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:if test="(EmbargoDate and ($unixTimestamp < EmbargoDate/@UnixTimestamp)) or CreatedAt">
|
|
||||||
<dates>
|
<dates>
|
||||||
<xsl:call-template name="RdrDate2" />
|
<xsl:call-template name="RdrDate2" />
|
||||||
</dates>
|
</dates>
|
||||||
</xsl:if>
|
|
||||||
<version>
|
<version>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="@Version">
|
<xsl:when test="@Version">
|
||||||
|
|
@ -117,46 +109,42 @@
|
||||||
<!-- <xsl:value-of select="@Type" /> -->
|
<!-- <xsl:value-of select="@Type" /> -->
|
||||||
</resourceType>
|
</resourceType>
|
||||||
|
|
||||||
<xsl:if test="normalize-space(@landingpage) != ''">
|
|
||||||
<alternateIdentifiers>
|
<alternateIdentifiers>
|
||||||
<xsl:call-template name="AlternateIdentifier" />
|
<xsl:call-template name="AlternateIdentifier" />
|
||||||
</alternateIdentifiers>
|
</alternateIdentifiers>
|
||||||
</xsl:if>
|
|
||||||
|
|
||||||
<xsl:if test="Reference[normalize-space(@Type) != '' and normalize-space(@Relation) != '']">
|
<xsl:if test="Reference">
|
||||||
<relatedIdentifiers>
|
<relatedIdentifiers>
|
||||||
<xsl:apply-templates select="Reference" mode="oai_datacite" />
|
<xsl:apply-templates select="Reference" mode="oai_datacite" />
|
||||||
</relatedIdentifiers>
|
</relatedIdentifiers>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:if test="Licence[normalize-space(@Name) != '' or normalize-space(@Url) != '']">
|
|
||||||
<rightsList>
|
<rightsList>
|
||||||
<xsl:apply-templates select="Licence" mode="oai_datacite" />
|
<xsl:apply-templates select="Licence" mode="oai_datacite" />
|
||||||
</rightsList>
|
</rightsList>
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="File">
|
|
||||||
<sizes>
|
<sizes>
|
||||||
<size>
|
<size>
|
||||||
<xsl:value-of select="count(File)" />
|
<xsl:value-of select="count(File)" />
|
||||||
<xsl:text> datasets</xsl:text>
|
<xsl:text> datasets</xsl:text>
|
||||||
</size>
|
</size>
|
||||||
</sizes>
|
</sizes>
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="File[normalize-space(@MimeType) != '']">
|
|
||||||
<formats>
|
<formats>
|
||||||
<xsl:apply-templates select="File/@MimeType" mode="oai_datacite" />
|
<xsl:apply-templates select="File/@MimeType" mode="oai_datacite" />
|
||||||
</formats>
|
</formats>
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="TitleAbstract[normalize-space(@Value) != ''] or TitleAbstractAdditional[normalize-space(@Value) != '']">
|
|
||||||
<descriptions>
|
<descriptions>
|
||||||
<xsl:apply-templates select="TitleAbstract" mode="oai_datacite" />
|
<xsl:apply-templates select="TitleAbstract" mode="oai_datacite" />
|
||||||
<xsl:apply-templates select="TitleAbstractAdditional" mode="oai_datacite" />
|
<xsl:apply-templates select="TitleAbstractAdditional" mode="oai_datacite" />
|
||||||
</descriptions>
|
</descriptions>
|
||||||
</xsl:if>
|
|
||||||
<xsl:if test="Coverage[normalize-space(@XMin) != '' and normalize-space(@XMax) != '' and normalize-space(@YMin) != '' and normalize-space(@YMax) != '']">
|
|
||||||
<geoLocations>
|
<geoLocations>
|
||||||
<xsl:apply-templates select="Coverage" mode="oai_datacite" />
|
<xsl:apply-templates select="Coverage" mode="oai_datacite" />
|
||||||
|
<!-- <geoLocation>
|
||||||
|
<geoLocationBox>
|
||||||
|
<westBoundLongitude>6.58987</westBoundLongitude>
|
||||||
|
<eastBoundLongitude>6.83639</eastBoundLongitude>
|
||||||
|
<southBoundLatitude>50.16</southBoundLatitude>
|
||||||
|
<northBoundLatitude>50.18691</northBoundLatitude>
|
||||||
|
</geoLocationBox>
|
||||||
|
</geoLocation> -->
|
||||||
</geoLocations>
|
</geoLocations>
|
||||||
</xsl:if>
|
|
||||||
</resource>
|
</resource>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
|
@ -188,40 +176,41 @@
|
||||||
|
|
||||||
<xsl:template match="Coverage" mode="oai_datacite"
|
<xsl:template match="Coverage" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@XMin) != '' and normalize-space(@XMax) != '' and normalize-space(@YMin) != '' and normalize-space(@YMax) != ''">
|
|
||||||
<geoLocation>
|
<geoLocation>
|
||||||
<geoLocationBox>
|
<geoLocationBox>
|
||||||
<westBoundLongitude><xsl:value-of select="@XMin" /></westBoundLongitude>
|
<westBoundLongitude>
|
||||||
<eastBoundLongitude><xsl:value-of select="@XMax" /></eastBoundLongitude>
|
<xsl:value-of select="@XMin" />
|
||||||
<southBoundLatitude><xsl:value-of select="@YMin" /></southBoundLatitude>
|
</westBoundLongitude>
|
||||||
<northBoundLatitude><xsl:value-of select="@YMax" /></northBoundLatitude>
|
<eastBoundLongitude>
|
||||||
|
<xsl:value-of select="@XMax" />
|
||||||
|
</eastBoundLongitude>
|
||||||
|
<southBoundLatitude>
|
||||||
|
<xsl:value-of select="@YMin" />
|
||||||
|
</southBoundLatitude>
|
||||||
|
<northBoundLatitude>
|
||||||
|
<xsl:value-of select="@YMax" />
|
||||||
|
</northBoundLatitude>
|
||||||
</geoLocationBox>
|
</geoLocationBox>
|
||||||
</geoLocation>
|
</geoLocation>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- TitleAbstract template -->
|
|
||||||
<xsl:template match="TitleAbstract" mode="oai_datacite"
|
<xsl:template match="TitleAbstract" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Value) != ''">
|
|
||||||
<description>
|
<description>
|
||||||
<xsl:attribute name="xml:lang">
|
<xsl:attribute name="xml:lang">
|
||||||
<xsl:value-of select="@Language" />
|
<xsl:value-of select="@Language" />
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
<xsl:if test="@Type != ''">
|
<xsl:if test="@Type != ''">
|
||||||
<xsl:attribute name="descriptionType">
|
<xsl:attribute name="descriptionType">
|
||||||
|
<!-- <xsl:value-of select="@Type" /> -->
|
||||||
<xsl:text>Abstract</xsl:text>
|
<xsl:text>Abstract</xsl:text>
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</description>
|
</description>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- TitleAbstractAdditional template -->
|
|
||||||
<xsl:template match="TitleAbstractAdditional" mode="oai_datacite"
|
<xsl:template match="TitleAbstractAdditional" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Value) != ''">
|
|
||||||
<description>
|
<description>
|
||||||
<xsl:attribute name="xml:lang">
|
<xsl:attribute name="xml:lang">
|
||||||
<xsl:value-of select="@Language" />
|
<xsl:value-of select="@Language" />
|
||||||
|
|
@ -235,7 +224,6 @@
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</description>
|
</description>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template name="CamelCaseWord">
|
<xsl:template name="CamelCaseWord">
|
||||||
|
|
@ -268,7 +256,6 @@
|
||||||
|
|
||||||
<xsl:template match="TitleMain" mode="oai_datacite"
|
<xsl:template match="TitleMain" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Value) != ''">
|
|
||||||
<title>
|
<title>
|
||||||
<xsl:if test="@Language != ''">
|
<xsl:if test="@Language != ''">
|
||||||
<xsl:attribute name="xml:lang">
|
<xsl:attribute name="xml:lang">
|
||||||
|
|
@ -282,12 +269,9 @@
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</title>
|
</title>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="TitleAdditional" mode="oai_datacite"
|
<xsl:template match="TitleAdditional" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Value) != ''">
|
|
||||||
<title>
|
<title>
|
||||||
<xsl:if test="@Language != ''">
|
<xsl:if test="@Language != ''">
|
||||||
<xsl:attribute name="xml:lang">
|
<xsl:attribute name="xml:lang">
|
||||||
|
|
@ -310,12 +294,10 @@
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</title>
|
</title>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="Subject" mode="oai_datacite"
|
<xsl:template match="Subject" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Value) != ''">
|
|
||||||
<subject>
|
<subject>
|
||||||
<xsl:if test="@Language != ''">
|
<xsl:if test="@Language != ''">
|
||||||
<xsl:attribute name="xml:lang">
|
<xsl:attribute name="xml:lang">
|
||||||
|
|
@ -324,24 +306,21 @@
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</subject>
|
</subject>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template name="AlternateIdentifier" match="AlternateIdentifier" mode="oai_datacite"
|
<xsl:template name="AlternateIdentifier" match="AlternateIdentifier" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@landingpage) != ''">
|
|
||||||
<alternateIdentifier>
|
<alternateIdentifier>
|
||||||
<xsl:attribute name="alternateIdentifierType">
|
<xsl:attribute name="alternateIdentifierType">
|
||||||
<xsl:text>url</xsl:text>
|
<xsl:text>url</xsl:text>
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
|
<!-- <xsl:variable name="identifier" select="concat($repURL, '/dataset/', @Id)" /> -->
|
||||||
<xsl:value-of select="@landingpage" />
|
<xsl:value-of select="@landingpage" />
|
||||||
</alternateIdentifier>
|
</alternateIdentifier>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="Reference" mode="oai_datacite"
|
<xsl:template match="Reference" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(@Type) != '' and normalize-space(@Relation) != ''">
|
|
||||||
<relatedIdentifier>
|
<relatedIdentifier>
|
||||||
<xsl:attribute name="relatedIdentifierType">
|
<xsl:attribute name="relatedIdentifierType">
|
||||||
<xsl:value-of select="@Type" />
|
<xsl:value-of select="@Type" />
|
||||||
|
|
@ -351,13 +330,10 @@
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
<xsl:value-of select="@Value" />
|
<xsl:value-of select="@Value" />
|
||||||
</relatedIdentifier>
|
</relatedIdentifier>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<!-- PersonContributor template -->
|
|
||||||
<xsl:template match="PersonContributor" mode="oai_datacite"
|
<xsl:template match="PersonContributor" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(concat(@FirstName, @LastName)) != ''">
|
|
||||||
<contributor>
|
<contributor>
|
||||||
<xsl:if test="@ContributorType != ''">
|
<xsl:if test="@ContributorType != ''">
|
||||||
<xsl:attribute name="contributorType">
|
<xsl:attribute name="contributorType">
|
||||||
|
|
@ -373,7 +349,6 @@
|
||||||
<xsl:value-of select="concat(@FirstName, ' ',@LastName)" />
|
<xsl:value-of select="concat(@FirstName, ' ',@LastName)" />
|
||||||
</contributorName>
|
</contributorName>
|
||||||
</contributor>
|
</contributor>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="PersonAuthor" mode="oai_datacite"
|
<xsl:template match="PersonAuthor" mode="oai_datacite"
|
||||||
|
|
@ -428,11 +403,9 @@
|
||||||
|
|
||||||
<xsl:template match="File/@MimeType" mode="oai_datacite"
|
<xsl:template match="File/@MimeType" mode="oai_datacite"
|
||||||
xmlns="http://datacite.org/schema/kernel-4">
|
xmlns="http://datacite.org/schema/kernel-4">
|
||||||
<xsl:if test="normalize-space(.) != ''">
|
|
||||||
<format>
|
<format>
|
||||||
<xsl:value-of select="." />
|
<xsl:value-of select="." />
|
||||||
</format>
|
</format>
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="Licence" mode="oai_datacite"
|
<xsl:template match="Licence" mode="oai_datacite"
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,6 @@ const handleSubmit = async (e) => {
|
||||||
</SectionTitleLineWithButton>
|
</SectionTitleLineWithButton>
|
||||||
<CardBox form @submit.prevent="handleSubmit">
|
<CardBox form @submit.prevent="handleSubmit">
|
||||||
<FormValidationErrors v-bind:errors="errors" />
|
<FormValidationErrors v-bind:errors="errors" />
|
||||||
<!-- Your existing warning display will show the warning -->
|
|
||||||
|
|
||||||
<!-- <div v-if="flash && flash.warning">
|
|
||||||
<p>{{ flash.warning }}</p>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- <div class="flex flex-col md:flex-row items-center"> -->
|
<!-- <div class="flex flex-col md:flex-row items-center"> -->
|
||||||
<!-- <div class="w-full">
|
<!-- <div class="w-full">
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,475 +0,0 @@
|
||||||
// ====================================================================
|
|
||||||
// FILE: composables/useDatasetChangeDetection.ts
|
|
||||||
// ====================================================================
|
|
||||||
|
|
||||||
import { computed, Ref } from 'vue';
|
|
||||||
import type { Dataset } from '@/Dataset';
|
|
||||||
import { InertiaForm } from '@inertiajs/vue3';
|
|
||||||
|
|
||||||
interface ComparisonOptions {
|
|
||||||
orderSensitive?: boolean;
|
|
||||||
compareKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDatasetChangeDetection(
|
|
||||||
form: InertiaForm<Dataset>,
|
|
||||||
originalDataset: Ref<Dataset>
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* Compare arrays with order sensitivity
|
|
||||||
*/
|
|
||||||
const compareArraysWithOrder = (
|
|
||||||
current: any[],
|
|
||||||
original: any[],
|
|
||||||
compareKey?: string
|
|
||||||
): boolean => {
|
|
||||||
if (current.length !== original.length) return true;
|
|
||||||
|
|
||||||
for (let i = 0; i < current.length; i++) {
|
|
||||||
const currentItem = current[i];
|
|
||||||
const originalItem = original[i];
|
|
||||||
|
|
||||||
if (compareKey && currentItem[compareKey] !== originalItem[compareKey]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON.stringify(currentItem) !== JSON.stringify(originalItem)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare arrays without order sensitivity (content-based)
|
|
||||||
*/
|
|
||||||
const compareArraysContent = (current: any[], original: any[]): boolean => {
|
|
||||||
if (current.length !== original.length) return true;
|
|
||||||
|
|
||||||
const normalizedCurrent = current
|
|
||||||
.map((item) => JSON.stringify(item))
|
|
||||||
.sort();
|
|
||||||
const normalizedOriginal = original
|
|
||||||
.map((item) => JSON.stringify(item))
|
|
||||||
.sort();
|
|
||||||
|
|
||||||
return (
|
|
||||||
JSON.stringify(normalizedCurrent) !== JSON.stringify(normalizedOriginal)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if licenses have changed
|
|
||||||
*/
|
|
||||||
const hasLicenseChanges = (): boolean => {
|
|
||||||
const originalLicenses = Array.isArray(originalDataset.value.licenses)
|
|
||||||
? originalDataset.value.licenses
|
|
||||||
.map((l) => (typeof l === 'object' ? l.id.toString() : String(l)))
|
|
||||||
.sort()
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const currentLicenses = Array.isArray(form.licenses)
|
|
||||||
? form.licenses
|
|
||||||
.map((l) => (typeof l === 'object' ? l.id.toString() : String(l)))
|
|
||||||
.sort()
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return JSON.stringify(currentLicenses) !== JSON.stringify(originalLicenses);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if basic properties have changed
|
|
||||||
*/
|
|
||||||
const hasBasicPropertyChanges = (): boolean => {
|
|
||||||
const original = originalDataset.value;
|
|
||||||
|
|
||||||
return (
|
|
||||||
form.language !== original.language ||
|
|
||||||
form.type !== original.type ||
|
|
||||||
form.creating_corporation !== original.creating_corporation ||
|
|
||||||
Number(form.project_id) !== Number(original.project_id) ||
|
|
||||||
form.embargo_date !== original.embargo_date
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there are items marked for deletion
|
|
||||||
*/
|
|
||||||
const hasDeletionChanges = (): boolean => {
|
|
||||||
return (
|
|
||||||
(form.subjectsToDelete?.length ?? 0) > 0 ||
|
|
||||||
(form.referencesToDelete?.length ?? 0) > 0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if files have changed
|
|
||||||
*/
|
|
||||||
const hasFileChanges = (): boolean => {
|
|
||||||
const currentFiles = form.files || [];
|
|
||||||
const originalFiles = originalDataset.value.files || [];
|
|
||||||
|
|
||||||
// Check for new files
|
|
||||||
const newFiles = currentFiles.filter((f) => !f.id);
|
|
||||||
if (newFiles.length > 0) return true;
|
|
||||||
|
|
||||||
// Check for deleted files
|
|
||||||
const originalFileIds = originalFiles.map((f) => f.id).filter(Boolean);
|
|
||||||
const currentFileIds = currentFiles.map((f) => f.id).filter(Boolean);
|
|
||||||
if (!originalFileIds.every((id) => currentFileIds.includes(id))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for file order changes
|
|
||||||
return compareArraysWithOrder(currentFiles, originalFiles, 'sort_order');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if coverage has changed
|
|
||||||
*/
|
|
||||||
const hasCoverageChanges = (): boolean => {
|
|
||||||
const currentCoverage = form.coverage || {};
|
|
||||||
const originalCoverage = originalDataset.value.coverage || {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
Number(currentCoverage.x_min) !== Number(originalCoverage.x_min) ||
|
|
||||||
Number(currentCoverage.x_max) !== Number(originalCoverage.x_max) ||
|
|
||||||
Number(currentCoverage.y_min) !== Number(originalCoverage.y_min) ||
|
|
||||||
Number(currentCoverage.y_max) !== Number(originalCoverage.y_max)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main change detection computed property
|
|
||||||
*/
|
|
||||||
const hasUnsavedChanges = computed(() => {
|
|
||||||
// Check if form is processing
|
|
||||||
if (form.processing) return true;
|
|
||||||
|
|
||||||
const original = originalDataset.value;
|
|
||||||
|
|
||||||
// Check basic properties
|
|
||||||
if (hasBasicPropertyChanges()) return true;
|
|
||||||
|
|
||||||
// Check deletion arrays
|
|
||||||
if (hasDeletionChanges()) return true;
|
|
||||||
|
|
||||||
// Check licenses
|
|
||||||
if (hasLicenseChanges()) return true;
|
|
||||||
|
|
||||||
// Check files (order-sensitive)
|
|
||||||
if (hasFileChanges()) return true;
|
|
||||||
|
|
||||||
// Check authors (order-sensitive)
|
|
||||||
if (
|
|
||||||
compareArraysWithOrder(
|
|
||||||
form.authors || [],
|
|
||||||
original.authors || []
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check contributors (order-sensitive)
|
|
||||||
if (
|
|
||||||
compareArraysWithOrder(
|
|
||||||
form.contributors || [],
|
|
||||||
original.contributors || []
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check titles (order-sensitive)
|
|
||||||
if (compareArraysWithOrder(form.titles, original.titles)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check descriptions (order-sensitive)
|
|
||||||
if (compareArraysWithOrder(form.descriptions, original.descriptions)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check subjects/keywords (order-insensitive)
|
|
||||||
if (
|
|
||||||
compareArraysContent(
|
|
||||||
form.subjects || [],
|
|
||||||
original.subjects || []
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check references (order-insensitive)
|
|
||||||
if (
|
|
||||||
compareArraysContent(
|
|
||||||
form.references || [],
|
|
||||||
original.references || []
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check coverage
|
|
||||||
if (hasCoverageChanges()) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Analyze array changes with detailed information
|
|
||||||
*/
|
|
||||||
const analyzeArrayChanges = (
|
|
||||||
current: any[],
|
|
||||||
original: any[],
|
|
||||||
itemName: string
|
|
||||||
): string[] => {
|
|
||||||
const changes: string[] = [];
|
|
||||||
|
|
||||||
// Check for count changes
|
|
||||||
if (current.length !== original.length) {
|
|
||||||
const diff = current.length - original.length;
|
|
||||||
if (diff > 0) {
|
|
||||||
changes.push(`${diff} ${itemName}(s) added`);
|
|
||||||
} else {
|
|
||||||
changes.push(`${Math.abs(diff)} ${itemName}(s) removed`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for order changes (only if same count)
|
|
||||||
if (current.length === original.length && current.length > 1) {
|
|
||||||
const currentIds = current.map((item) => item.id).filter(Boolean);
|
|
||||||
const originalIds = original.map((item) => item.id).filter(Boolean);
|
|
||||||
|
|
||||||
if (currentIds.length === originalIds.length && currentIds.length > 0) {
|
|
||||||
const orderChanged = currentIds.some(
|
|
||||||
(id, index) => id !== originalIds[index]
|
|
||||||
);
|
|
||||||
if (orderChanged) {
|
|
||||||
changes.push(`${itemName} order changed`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for content changes
|
|
||||||
if (current.length === original.length) {
|
|
||||||
const contentChanged =
|
|
||||||
JSON.stringify(current) !== JSON.stringify(original);
|
|
||||||
const orderChanged = changes.some((change) =>
|
|
||||||
change.includes('order changed')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (contentChanged && !orderChanged) {
|
|
||||||
changes.push(`${itemName} content modified`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate detailed changes summary
|
|
||||||
*/
|
|
||||||
const getChangesSummary = (): string[] => {
|
|
||||||
const changes: string[] = [];
|
|
||||||
const original = originalDataset.value;
|
|
||||||
|
|
||||||
// Basic property changes
|
|
||||||
if (form.language !== original.language) {
|
|
||||||
changes.push('Language changed');
|
|
||||||
}
|
|
||||||
if (form.type !== original.type) {
|
|
||||||
changes.push('Dataset type changed');
|
|
||||||
}
|
|
||||||
if (form.creating_corporation !== original.creating_corporation) {
|
|
||||||
changes.push('Creating corporation changed');
|
|
||||||
}
|
|
||||||
if (Number(form.project_id) !== Number(original.project_id)) {
|
|
||||||
changes.push('Project changed');
|
|
||||||
}
|
|
||||||
if (form.embargo_date !== original.embargo_date) {
|
|
||||||
changes.push('Embargo date changed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deletion tracking
|
|
||||||
if ((form.subjectsToDelete?.length ?? 0) > 0) {
|
|
||||||
changes.push(
|
|
||||||
`${form.subjectsToDelete.length} keyword(s) marked for deletion`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ((form.referencesToDelete?.length ?? 0) > 0) {
|
|
||||||
changes.push(
|
|
||||||
`${form.referencesToDelete.length} reference(s) marked for deletion`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// License changes
|
|
||||||
if (hasLicenseChanges()) {
|
|
||||||
changes.push('Licenses modified');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Files analysis
|
|
||||||
const currentFiles = form.files || [];
|
|
||||||
const originalFiles = original.files || [];
|
|
||||||
const newFiles = currentFiles.filter((f) => !f.id);
|
|
||||||
|
|
||||||
if (newFiles.length > 0) {
|
|
||||||
changes.push(`${newFiles.length} new file(s) added`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingCurrentFiles = currentFiles.filter((f) => f.id);
|
|
||||||
const existingOriginalFiles = originalFiles.filter((f) => f.id);
|
|
||||||
|
|
||||||
if (
|
|
||||||
existingCurrentFiles.length === existingOriginalFiles.length &&
|
|
||||||
existingCurrentFiles.length > 1
|
|
||||||
) {
|
|
||||||
const currentOrder = existingCurrentFiles.map((f) => f.id);
|
|
||||||
const originalOrder = existingOriginalFiles.map((f) => f.id);
|
|
||||||
const orderChanged = currentOrder.some(
|
|
||||||
(id, index) => id !== originalOrder[index]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (orderChanged) {
|
|
||||||
changes.push('File order changed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authors and contributors
|
|
||||||
changes.push(
|
|
||||||
...analyzeArrayChanges(
|
|
||||||
form.authors || [],
|
|
||||||
original.authors || [],
|
|
||||||
'author'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
changes.push(
|
|
||||||
...analyzeArrayChanges(
|
|
||||||
form.contributors || [],
|
|
||||||
original.contributors || [],
|
|
||||||
'contributor'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Titles analysis
|
|
||||||
if (JSON.stringify(form.titles) !== JSON.stringify(original.titles)) {
|
|
||||||
if (form.titles.length !== original.titles.length) {
|
|
||||||
const diff = form.titles.length - original.titles.length;
|
|
||||||
changes.push(
|
|
||||||
diff > 0
|
|
||||||
? `${diff} title(s) added`
|
|
||||||
: `${Math.abs(diff)} title(s) removed`
|
|
||||||
);
|
|
||||||
} else if (form.titles.length > 0) {
|
|
||||||
if (form.titles[0]?.value !== original.titles[0]?.value) {
|
|
||||||
changes.push('Main title changed');
|
|
||||||
}
|
|
||||||
const otherTitlesChanged = form.titles
|
|
||||||
.slice(1)
|
|
||||||
.some(
|
|
||||||
(title, index) =>
|
|
||||||
JSON.stringify(title) !== JSON.stringify(original.titles[index + 1])
|
|
||||||
);
|
|
||||||
if (otherTitlesChanged) {
|
|
||||||
changes.push('Additional titles modified');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Descriptions analysis
|
|
||||||
if (
|
|
||||||
JSON.stringify(form.descriptions) !==
|
|
||||||
JSON.stringify(original.descriptions)
|
|
||||||
) {
|
|
||||||
if (form.descriptions.length !== original.descriptions.length) {
|
|
||||||
const diff = form.descriptions.length - original.descriptions.length;
|
|
||||||
changes.push(
|
|
||||||
diff > 0
|
|
||||||
? `${diff} description(s) added`
|
|
||||||
: `${Math.abs(diff)} description(s) removed`
|
|
||||||
);
|
|
||||||
} else if (form.descriptions.length > 0) {
|
|
||||||
if (
|
|
||||||
form.descriptions[0]?.value !== original.descriptions[0]?.value
|
|
||||||
) {
|
|
||||||
changes.push('Main abstract changed');
|
|
||||||
}
|
|
||||||
const otherDescChanged = form.descriptions
|
|
||||||
.slice(1)
|
|
||||||
.some(
|
|
||||||
(desc, index) =>
|
|
||||||
JSON.stringify(desc) !==
|
|
||||||
JSON.stringify(original.descriptions[index + 1])
|
|
||||||
);
|
|
||||||
if (otherDescChanged) {
|
|
||||||
changes.push('Additional descriptions modified');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subjects/Keywords analysis
|
|
||||||
const currentSubjects = form.subjects || [];
|
|
||||||
const originalSubjects = original.subjects || [];
|
|
||||||
if (currentSubjects.length !== originalSubjects.length) {
|
|
||||||
const diff = currentSubjects.length - originalSubjects.length;
|
|
||||||
changes.push(
|
|
||||||
diff > 0
|
|
||||||
? `${diff} keyword(s) added`
|
|
||||||
: `${Math.abs(diff)} keyword(s) removed`
|
|
||||||
);
|
|
||||||
} else if (currentSubjects.length > 0) {
|
|
||||||
const currentSubjectsNormalized = currentSubjects
|
|
||||||
.map((s) => JSON.stringify(s))
|
|
||||||
.sort();
|
|
||||||
const originalSubjectsNormalized = originalSubjects
|
|
||||||
.map((s) => JSON.stringify(s))
|
|
||||||
.sort();
|
|
||||||
if (
|
|
||||||
JSON.stringify(currentSubjectsNormalized) !==
|
|
||||||
JSON.stringify(originalSubjectsNormalized)
|
|
||||||
) {
|
|
||||||
changes.push('Keywords modified');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// References analysis
|
|
||||||
const currentRefs = form.references || [];
|
|
||||||
const originalRefs = original.references || [];
|
|
||||||
if (currentRefs.length !== originalRefs.length) {
|
|
||||||
const diff = currentRefs.length - originalRefs.length;
|
|
||||||
changes.push(
|
|
||||||
diff > 0
|
|
||||||
? `${diff} reference(s) added`
|
|
||||||
: `${Math.abs(diff)} reference(s) removed`
|
|
||||||
);
|
|
||||||
} else if (currentRefs.length > 0) {
|
|
||||||
const currentRefsNormalized = currentRefs
|
|
||||||
.map((r) => JSON.stringify(r))
|
|
||||||
.sort();
|
|
||||||
const originalRefsNormalized = originalRefs
|
|
||||||
.map((r) => JSON.stringify(r))
|
|
||||||
.sort();
|
|
||||||
if (
|
|
||||||
JSON.stringify(currentRefsNormalized) !==
|
|
||||||
JSON.stringify(originalRefsNormalized)
|
|
||||||
) {
|
|
||||||
changes.push('References modified');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coverage changes
|
|
||||||
if (hasCoverageChanges()) {
|
|
||||||
changes.push('Geographic coverage changed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
hasUnsavedChanges,
|
|
||||||
getChangesSummary,
|
|
||||||
compareArraysWithOrder,
|
|
||||||
compareArraysContent,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,217 +0,0 @@
|
||||||
// ====================================================================
|
|
||||||
// FILE: composables/useDatasetFormSubmission.ts
|
|
||||||
// ====================================================================
|
|
||||||
|
|
||||||
import { Ref } from 'vue';
|
|
||||||
import type { Dataset, License } from '@/Dataset';
|
|
||||||
import { InertiaForm } from '@inertiajs/vue3';
|
|
||||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
|
||||||
import { notify } from '@/notiwind';
|
|
||||||
|
|
||||||
interface SubmissionOptions {
|
|
||||||
onSuccess?: (updatedDataset: Dataset) => void;
|
|
||||||
onError?: (errors: any) => void;
|
|
||||||
showNotification?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDatasetFormSubmission(
|
|
||||||
form: InertiaForm<Dataset>,
|
|
||||||
originalDataset: Ref<Dataset>
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* Check if object has id attribute (type guard)
|
|
||||||
*/
|
|
||||||
const hasIdAttribute = (obj: License | number): obj is License => {
|
|
||||||
return typeof obj === 'object' && 'id' in obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform licenses for submission
|
|
||||||
*/
|
|
||||||
const transformLicenses = (): string[] => {
|
|
||||||
return form.licenses.map((obj) => {
|
|
||||||
if (hasIdAttribute(obj)) {
|
|
||||||
return obj.id.toString();
|
|
||||||
}
|
|
||||||
return String(obj);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate form before submission
|
|
||||||
*/
|
|
||||||
const validateForm = (): { valid: boolean; errors: string[] } => {
|
|
||||||
const errors: string[] = [];
|
|
||||||
|
|
||||||
// Required field validations
|
|
||||||
if (!form.language) {
|
|
||||||
errors.push('Language is required');
|
|
||||||
}
|
|
||||||
if (!form.type) {
|
|
||||||
errors.push('Dataset type is required');
|
|
||||||
}
|
|
||||||
if (!form.creating_corporation) {
|
|
||||||
errors.push('Creating corporation is required');
|
|
||||||
}
|
|
||||||
if (!form.titles || !form.titles[0]?.value) {
|
|
||||||
errors.push('Main title is required');
|
|
||||||
}
|
|
||||||
if (!form.descriptions || !form.descriptions[0]?.value) {
|
|
||||||
errors.push('Main abstract is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
valid: errors.length === 0,
|
|
||||||
errors,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle successful submission
|
|
||||||
*/
|
|
||||||
const handleSubmitSuccess = (
|
|
||||||
updatedDataset: Dataset,
|
|
||||||
showNotification: boolean = true
|
|
||||||
) => {
|
|
||||||
// Clear deletion arrays
|
|
||||||
if (updatedDataset.subjectsToDelete) {
|
|
||||||
updatedDataset.subjectsToDelete = [];
|
|
||||||
}
|
|
||||||
if (updatedDataset.referencesToDelete) {
|
|
||||||
updatedDataset.referencesToDelete = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update form with fresh data from server
|
|
||||||
Object.keys(updatedDataset).forEach((key) => {
|
|
||||||
if (key !== 'licenses' && key in form) {
|
|
||||||
form[key] = updatedDataset[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear form errors
|
|
||||||
form.clearErrors();
|
|
||||||
|
|
||||||
// Update original dataset reference
|
|
||||||
originalDataset.value = JSON.parse(JSON.stringify(updatedDataset));
|
|
||||||
|
|
||||||
// Show success notification
|
|
||||||
if (showNotification) {
|
|
||||||
notify(
|
|
||||||
{
|
|
||||||
type: 'success',
|
|
||||||
title: 'Success',
|
|
||||||
text: 'Dataset updated successfully',
|
|
||||||
},
|
|
||||||
4000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle submission errors
|
|
||||||
*/
|
|
||||||
const handleSubmitError = (errors: any) => {
|
|
||||||
console.error('Submission errors:', errors);
|
|
||||||
|
|
||||||
notify(
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
title: 'Error',
|
|
||||||
text: 'Failed to update dataset. Please check the form for errors.',
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit form with auto-save behavior
|
|
||||||
*/
|
|
||||||
const submitWithAutoSave = async (
|
|
||||||
options: SubmissionOptions = {}
|
|
||||||
): Promise<void> => {
|
|
||||||
try {
|
|
||||||
const route = stardust.route('editor.dataset.update', [form.id]);
|
|
||||||
const licenses = transformLicenses();
|
|
||||||
|
|
||||||
await form
|
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
licenses,
|
|
||||||
rights: 'true',
|
|
||||||
}))
|
|
||||||
.put(route, {
|
|
||||||
onSuccess: (page) => {
|
|
||||||
const updatedDataset = page.props.dataset || form.data();
|
|
||||||
handleSubmitSuccess(
|
|
||||||
updatedDataset,
|
|
||||||
options.showNotification ?? true
|
|
||||||
);
|
|
||||||
|
|
||||||
if (options.onSuccess) {
|
|
||||||
options.onSuccess(updatedDataset);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (errors) => {
|
|
||||||
handleSubmitError(errors);
|
|
||||||
|
|
||||||
if (options.onError) {
|
|
||||||
options.onError(errors);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Unexpected error during submission:', error);
|
|
||||||
notify(
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
title: 'Error',
|
|
||||||
text: 'An unexpected error occurred. Please try again.',
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard submit with validation
|
|
||||||
*/
|
|
||||||
const submit = async (
|
|
||||||
options: SubmissionOptions = {}
|
|
||||||
): Promise<void> => {
|
|
||||||
// Validate form first
|
|
||||||
const validation = validateForm();
|
|
||||||
if (!validation.valid) {
|
|
||||||
notify(
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
title: 'Validation Error',
|
|
||||||
text: validation.errors.join(', '),
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await submitWithAutoSave({
|
|
||||||
...options,
|
|
||||||
showNotification: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Silent submit without notification (for auto-save)
|
|
||||||
*/
|
|
||||||
const submitSilently = async (): Promise<void> => {
|
|
||||||
await submitWithAutoSave({
|
|
||||||
showNotification: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
submit,
|
|
||||||
submitWithAutoSave,
|
|
||||||
submitSilently,
|
|
||||||
validateForm,
|
|
||||||
transformLicenses,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue