mirror of
https://github.com/modrinth/code.git
synced 2026-09-05 06:19:11 +00:00
feat: hide validation error when field has been edited
This commit is contained in:
@@ -28,8 +28,8 @@
|
||||
<script setup lang="ts">
|
||||
import { LightBulbIcon, TriangleAlertIcon, XCircleIcon } from '@modrinth/assets'
|
||||
import type { FieldValidationMessage } from '@modrinth/moderation'
|
||||
import { useVIntl } from '@modrinth/ui'
|
||||
import { computed, onScopeDispose, shallowRef, watch } from 'vue'
|
||||
import { injectProjectPageContext, useVIntl } from '@modrinth/ui'
|
||||
import { computed, onScopeDispose, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
type ValidationCheck = Omit<FieldValidationMessage, 'code'> & { code?: string }
|
||||
|
||||
@@ -39,6 +39,8 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
check?: ValidationCheckInput
|
||||
debounce?: number
|
||||
projectField?: unknown
|
||||
currentField?: unknown
|
||||
}>(),
|
||||
{
|
||||
check: null,
|
||||
@@ -47,31 +49,65 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { projectValidationLoading } = injectProjectPageContext()
|
||||
const displayedCheck = shallowRef<ValidationCheckInput>(props.check)
|
||||
const validationIsStale = ref(props.projectField !== props.currentField)
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
||||
let validationRefreshCompleted = false
|
||||
|
||||
function updateDisplayedCheck(check: ValidationCheckInput) {
|
||||
clearTimeout(debounceTimer)
|
||||
const update = () => {
|
||||
displayedCheck.value = check
|
||||
if (
|
||||
validationRefreshCompleted &&
|
||||
!projectValidationLoading.value &&
|
||||
props.projectField === props.currentField
|
||||
) {
|
||||
validationIsStale.value = false
|
||||
validationRefreshCompleted = false
|
||||
}
|
||||
}
|
||||
|
||||
if (props.debounce <= 0) {
|
||||
update()
|
||||
return
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(update, props.debounce)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.check,
|
||||
(check) => {
|
||||
clearTimeout(debounceTimer)
|
||||
if (props.debounce <= 0) {
|
||||
displayedCheck.value = check
|
||||
return
|
||||
() => [props.projectField, props.currentField] as const,
|
||||
([projectField, currentField], [previousProjectField]) => {
|
||||
if (projectField !== currentField) {
|
||||
validationIsStale.value = true
|
||||
validationRefreshCompleted = false
|
||||
} else if (projectField === previousProjectField) {
|
||||
validationIsStale.value = false
|
||||
validationRefreshCompleted = false
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(() => {
|
||||
displayedCheck.value = check
|
||||
}, props.debounce)
|
||||
},
|
||||
)
|
||||
|
||||
watch(projectValidationLoading, (loading, wasLoading) => {
|
||||
if (!loading && wasLoading) {
|
||||
validationRefreshCompleted = true
|
||||
updateDisplayedCheck(props.check)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.check, updateDisplayedCheck)
|
||||
|
||||
onScopeDispose(() => clearTimeout(debounceTimer))
|
||||
|
||||
const validations = computed(() =>
|
||||
Array.isArray(displayedCheck.value)
|
||||
const validations = computed(() => {
|
||||
if (validationIsStale.value || projectValidationLoading.value) return []
|
||||
|
||||
return Array.isArray(displayedCheck.value)
|
||||
? displayedCheck.value
|
||||
: displayedCheck.value
|
||||
? [displayedCheck.value]
|
||||
: [],
|
||||
)
|
||||
: []
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -842,6 +842,14 @@ const messages = defineMessages({
|
||||
id: 'project.notification.updated.message',
|
||||
defaultMessage: 'Your project has been updated.',
|
||||
},
|
||||
projectReviewSaveFailed: {
|
||||
id: 'project.notification.review-save-failed.title',
|
||||
defaultMessage: 'Failed to save project in review',
|
||||
},
|
||||
projectReviewSaveFailedDescription: {
|
||||
id: 'project.notification.review-save-failed.description',
|
||||
defaultMessage: 'You cannot save edits to your project which result in failing validation.',
|
||||
},
|
||||
reviewEnvironmentSettings: {
|
||||
id: 'project.environment.migration.review-button',
|
||||
defaultMessage: 'Review environment settings',
|
||||
@@ -1377,6 +1385,30 @@ function mergeV3ProjectPatch(old, data) {
|
||||
return merged
|
||||
}
|
||||
|
||||
const PROJECT_REVIEW_VALIDATION_ERROR =
|
||||
'project must have no required validation nags before or while under review'
|
||||
|
||||
function addProjectMutationErrorNotification(error) {
|
||||
const description =
|
||||
error?.v1Error?.description ??
|
||||
error?.responseData?.description ??
|
||||
error?.data?.description ??
|
||||
error?.message
|
||||
const isProjectReviewValidationError = description === PROJECT_REVIEW_VALIDATION_ERROR
|
||||
|
||||
addNotification({
|
||||
title: formatMessage(
|
||||
isProjectReviewValidationError
|
||||
? messages.projectReviewSaveFailed
|
||||
: commonMessages.errorNotificationTitle,
|
||||
),
|
||||
text: isProjectReviewValidationError
|
||||
? formatMessage(messages.projectReviewSaveFailedDescription)
|
||||
: description,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
// Mutation for patching project data
|
||||
const patchProjectMutation = useMutation({
|
||||
mutationFn: async ({ projectId, data }) => {
|
||||
@@ -1412,11 +1444,7 @@ const patchProjectMutation = useMutation({
|
||||
if (context?.previousV3) {
|
||||
queryClient.setQueryData(['project', 'v3', context.projectId], context.previousV3)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
@@ -1447,11 +1475,7 @@ const patchStatusMutation = useMutation({
|
||||
if (context?.previousProject) {
|
||||
queryClient.setQueryData(['project', 'v2', context.projectId], context.previousProject)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
@@ -1491,11 +1515,7 @@ const patchProjectV3Mutation = useMutation({
|
||||
if (context?.previousV2) {
|
||||
queryClient.setQueryData(['project', 'v2', context.projectId], context.previousV2)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
@@ -1519,11 +1539,7 @@ const patchIconMutation = useMutation({
|
||||
},
|
||||
|
||||
onError: (err) => {
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
@@ -1572,11 +1588,7 @@ const createGalleryItemMutation = useMutation({
|
||||
if (context?.previousProject) {
|
||||
queryClient.setQueryData(['project', 'v2', context.projectId], context.previousProject)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
@@ -1625,11 +1637,7 @@ const editGalleryItemMutation = useMutation({
|
||||
if (context?.previousProject) {
|
||||
queryClient.setQueryData(['project', 'v2', context.projectId], context.previousProject)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
@@ -1662,11 +1670,7 @@ const deleteGalleryItemMutation = useMutation({
|
||||
if (context?.previousProject) {
|
||||
queryClient.setQueryData(['project', 'v2', context.projectId], context.previousProject)
|
||||
}
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: err.data ? err.data.description : err.message,
|
||||
type: 'error',
|
||||
})
|
||||
addProjectMutationErrorNotification(err)
|
||||
},
|
||||
|
||||
onSettled: async () => {
|
||||
|
||||
@@ -44,7 +44,11 @@
|
||||
:maxlength="64"
|
||||
placeholder="Enter title..."
|
||||
/>
|
||||
<ValidationMessage :check="galleryTitleValidation" />
|
||||
<ValidationMessage
|
||||
:check="galleryTitleValidation"
|
||||
:project-field="filteredGallery[editIndex]?.title ?? ''"
|
||||
:current-field="editTitle"
|
||||
/>
|
||||
<label for="gallery-image-desc">
|
||||
<span class="label__title">Description</span>
|
||||
</label>
|
||||
@@ -54,7 +58,11 @@
|
||||
:maxlength="255"
|
||||
placeholder="Enter description..."
|
||||
/>
|
||||
<ValidationMessage :check="galleryDescriptionValidation" />
|
||||
<ValidationMessage
|
||||
:check="galleryDescriptionValidation"
|
||||
:project-field="filteredGallery[editIndex]?.description ?? ''"
|
||||
:current-field="editDescription"
|
||||
/>
|
||||
<label for="gallery-image-ordering">
|
||||
<span class="label__title">Order Index</span>
|
||||
</label>
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
:disabled="!hasPermission"
|
||||
:on-image-upload="onUploadHandler"
|
||||
/>
|
||||
<ValidationMessage :check="descriptionValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="descriptionValidation"
|
||||
:project-field="saved.description"
|
||||
:current-field="current.description"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<UnsavedChangesPopup
|
||||
:original="saved"
|
||||
|
||||
@@ -294,6 +294,8 @@ const { confirmLeaveModal } = usePageLeaveSafety(hasChanges)
|
||||
</p>
|
||||
<ValidationMessage
|
||||
:check="[...disclosureValidation, ...disclosureTextValidation]"
|
||||
:project-field="JSON.stringify(savedSnapshot)"
|
||||
:current-field="JSON.stringify(currentSnapshot)"
|
||||
class="mb-4"
|
||||
/>
|
||||
<EmptyState
|
||||
|
||||
@@ -45,7 +45,12 @@
|
||||
:maxlength="64"
|
||||
placeholder="Enter title..."
|
||||
/>
|
||||
<ValidationMessage class="mt-2" :check="galleryTitleValidation" />
|
||||
<ValidationMessage
|
||||
class="mt-2 max-w-[550px]"
|
||||
:check="galleryTitleValidation"
|
||||
:project-field="filteredGallery[editIndex]?.title ?? ''"
|
||||
:current-field="editTitle"
|
||||
/>
|
||||
<label for="gallery-image-desc">
|
||||
<span class="label__title">Description</span>
|
||||
</label>
|
||||
@@ -55,7 +60,12 @@
|
||||
:maxlength="255"
|
||||
placeholder="Enter description..."
|
||||
/>
|
||||
<ValidationMessage class="mt-2" :check="galleryDescriptionValidation" />
|
||||
<ValidationMessage
|
||||
class="mt-2 max-w-[550px]"
|
||||
:check="galleryDescriptionValidation"
|
||||
:project-field="filteredGallery[editIndex]?.description ?? ''"
|
||||
:current-field="editDescription"
|
||||
/>
|
||||
<label for="gallery-image-ordering">
|
||||
<span class="label__title">Order Index</span>
|
||||
</label>
|
||||
|
||||
@@ -179,7 +179,12 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
<div class="base-card block">
|
||||
<div class="group relative float-end ml-4">
|
||||
<IconSelect v-model="current.icon" />
|
||||
<ValidationMessage :check="iconValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="iconValidation"
|
||||
:project-field="saved.icon"
|
||||
:current-field="current.icon"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<SettingsLabel
|
||||
@@ -197,7 +202,12 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
wrapper-class="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
<ValidationMessage :check="titleValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="titleValidation"
|
||||
:project-field="saved.title"
|
||||
:current-field="current.title"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<SettingsLabel
|
||||
@@ -213,7 +223,12 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
:maxlength="120"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
<ValidationMessage :check="taglineValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="taglineValidation"
|
||||
:project-field="saved.tagline"
|
||||
:current-field="current.tagline"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4" @focusin="onSlugSuggestionFocusIn" @focusout="onSlugSuggestionFocusOut">
|
||||
<SettingsLabel id="project-url" :title="messages.urlTitle" />
|
||||
|
||||
@@ -29,7 +29,12 @@
|
||||
wrapper-class="w-full max-w-72"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="nameValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="nameValidation"
|
||||
:project-field="project.name"
|
||||
:current-field="name"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div @focusin="onSlugSuggestionFocusIn" @focusout="onSlugSuggestionFocusOut">
|
||||
@@ -69,7 +74,12 @@
|
||||
:disabled="!hasPermission"
|
||||
resize="vertical"
|
||||
/>
|
||||
<ValidationMessage :check="summaryValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="summaryValidation"
|
||||
:project-field="project.summary"
|
||||
:current-field="summary"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -109,7 +119,12 @@
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<ValidationMessage :check="iconValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="iconValidation"
|
||||
:project-field="false"
|
||||
:current-field="Boolean(icon || deletedIcon)"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Server Project Settings -->
|
||||
|
||||
@@ -36,7 +36,12 @@
|
||||
:disabled="!hasPermission"
|
||||
trigger-type="base"
|
||||
/>
|
||||
<ValidationMessage :check="licenseSelectionValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="licenseSelectionValidation"
|
||||
:project-field="project.license.id"
|
||||
:current-field="licenseId"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,8 +90,16 @@
|
||||
:disabled="!hasPermission || licenseId === 'LicenseRef-Unknown'"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
<ValidationMessage :check="customLicenseValidation" />
|
||||
<ValidationMessage :check="effectiveLicenseCheck" />
|
||||
<ValidationMessage
|
||||
:check="customLicenseValidation"
|
||||
:project-field="project.license.id"
|
||||
:current-field="licenseId"
|
||||
/>
|
||||
<ValidationMessage
|
||||
:check="effectiveLicenseCheck"
|
||||
:project-field="project.license.url ?? ''"
|
||||
:current-field="current.licenseUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
<!-- Server Project Links -->
|
||||
<section v-if="isServerProject" class="universal-card">
|
||||
<h2>External links</h2>
|
||||
<ValidationMessage :check="externalLinksValidation" class="mb-4" />
|
||||
<ValidationMessage
|
||||
:check="externalLinksValidation"
|
||||
:project-field="JSON.stringify(saved)"
|
||||
:current-field="JSON.stringify(current)"
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="adjacent-input">
|
||||
<label id="server-website" title="Your server's website.">
|
||||
<span class="label__title">Website</span>
|
||||
@@ -18,7 +23,11 @@
|
||||
maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="siteCheck" />
|
||||
<ValidationMessage
|
||||
:check="siteCheck"
|
||||
:project-field="saved.site"
|
||||
:current-field="current.site"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label id="server-store" title="Your server's store page.">
|
||||
@@ -33,7 +42,11 @@
|
||||
maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="storeCheck" />
|
||||
<ValidationMessage
|
||||
:check="storeCheck"
|
||||
:project-field="saved.store"
|
||||
:current-field="current.store"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label
|
||||
@@ -53,7 +66,11 @@
|
||||
maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="wikiCheck" />
|
||||
<ValidationMessage
|
||||
:check="wikiCheck"
|
||||
:project-field="saved.wiki"
|
||||
:current-field="current.wiki"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label id="server-discord" title="An invitation link to your Discord server.">
|
||||
@@ -68,14 +85,23 @@
|
||||
maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="discordInviteCheck" />
|
||||
<ValidationMessage
|
||||
:check="discordInviteCheck"
|
||||
:project-field="saved.discord"
|
||||
:current-field="current.discord"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Standard Project Links -->
|
||||
<section v-if="!isServerProject" class="universal-card">
|
||||
<h2>External links</h2>
|
||||
<ValidationMessage :check="externalLinksValidation" class="mb-4" />
|
||||
<ValidationMessage
|
||||
:check="externalLinksValidation"
|
||||
:project-field="JSON.stringify(saved)"
|
||||
:current-field="JSON.stringify(current)"
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="adjacent-input">
|
||||
<label
|
||||
id="project-issue-tracker"
|
||||
@@ -94,7 +120,11 @@
|
||||
:maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="issuesCheck" />
|
||||
<ValidationMessage
|
||||
:check="issuesCheck"
|
||||
:project-field="saved.issues"
|
||||
:current-field="current.issues"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label
|
||||
@@ -114,7 +144,11 @@
|
||||
placeholder="Enter a valid URL"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="sourceCheck" />
|
||||
<ValidationMessage
|
||||
:check="sourceCheck"
|
||||
:project-field="saved.source"
|
||||
:current-field="current.source"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label
|
||||
@@ -134,7 +168,11 @@
|
||||
placeholder="Enter a valid URL"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="wikiCheck" />
|
||||
<ValidationMessage
|
||||
:check="wikiCheck"
|
||||
:project-field="saved.wiki"
|
||||
:current-field="current.wiki"
|
||||
/>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<label id="project-discord-invite" title="An invitation link to your Discord server.">
|
||||
@@ -149,7 +187,11 @@
|
||||
placeholder="Enter a valid URL"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="discordInviteCheck" />
|
||||
<ValidationMessage
|
||||
:check="discordInviteCheck"
|
||||
:project-field="saved.discord"
|
||||
:current-field="current.discord"
|
||||
/>
|
||||
</div>
|
||||
<span class="label">
|
||||
<span class="label__title">Donation links</span>
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
:placeholder="formatMessage(messages.selectRegionPlaceholder)"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="regionValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="regionValidation"
|
||||
:project-field="projectV3?.minecraft_server?.region ?? ''"
|
||||
:current-field="region"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Language -->
|
||||
@@ -43,7 +48,14 @@
|
||||
:placeholder="formatMessage(messages.selectLanguagesPlaceholder)"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<ValidationMessage :check="languageValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="languageValidation"
|
||||
:project-field="
|
||||
JSON.stringify([...(projectV3?.minecraft_server?.languages ?? [])].sort())
|
||||
"
|
||||
:current-field="JSON.stringify([...languages].sort())"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Java Address -->
|
||||
@@ -129,7 +141,12 @@
|
||||
/></template>
|
||||
</IntlFormatted>
|
||||
</div>
|
||||
<ValidationMessage :check="javaAddressValidation" class="mt-2" />
|
||||
<ValidationMessage
|
||||
:check="javaAddressValidation"
|
||||
:project-field="projectV3?.minecraft_java_server?.address ?? ''"
|
||||
:current-field="javaAddress.trim()"
|
||||
class="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Bedrock Address -->
|
||||
|
||||
@@ -409,7 +409,11 @@ const toggleFeatured = (tag: string) => {
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<ValidationMessage :check="tagValidation" />
|
||||
<ValidationMessage
|
||||
:check="tagValidation"
|
||||
:project-field="JSON.stringify(saved)"
|
||||
:current-field="JSON.stringify(current)"
|
||||
/>
|
||||
</div>
|
||||
<UnsavedChangesPopup
|
||||
:original="saved"
|
||||
|
||||
Reference in New Issue
Block a user