2024-01-06 15:09:26 -08:00
< template >
2025-08-14 21:48:38 +01:00
< NewModal ref = "modal" header = "Creating an organization" >
< div class = "flex flex-col gap-3" >
2025-09-25 18:55:57 -07:00
< Admonition
v - if = "disabled"
class = "max-w-[30rem]"
: type = "'info'"
header = "Organization creation temporarily disabled"
body = "Due to abuse, we've temporarily disabled the creation of new organizations. We're working hard to restore this feature, thank you for your patience."
/ >
< div class = "flex flex-col gap-2" : class = "{ 'opacity-50': disabled }" >
2025-08-14 21:48:38 +01:00
< label for = "name" >
< span class = "text-lg font-semibold text-contrast" >
Name
< span class = "text-brand-red" > * < / span >
< / span >
< / label >
< input
id = "name"
v - model = "name"
type = "text"
maxlength = "64"
: placeholder = "`Enter organization name...`"
autocomplete = "off"
2025-09-25 18:55:57 -07:00
: disabled = "disabled"
2025-08-14 21:48:38 +01:00
@ input = "updateSlug"
/ >
< / div >
2025-09-25 18:55:57 -07:00
< div class = "flex flex-col gap-2" : class = "{ 'opacity-50': disabled }" >
2025-08-14 21:48:38 +01:00
< label for = "slug" >
< span class = "text-lg font-semibold text-contrast" >
URL
< span class = "text-brand-red" > * < / span >
< / span >
< / label >
< div class = "text-input-wrapper" >
< div class = "text-input-wrapper__before" > https : //modrinth.com/organization/</div>
< input
id = "slug"
v - model = "slug"
type = "text"
maxlength = "64"
autocomplete = "off"
2025-09-25 18:55:57 -07:00
: disabled = "disabled"
2025-08-14 21:48:38 +01:00
@ input = "setManualSlug"
/ >
< / div >
< / div >
2025-09-25 18:55:57 -07:00
< div class = "flex flex-col gap-2" : class = "{ 'opacity-50': disabled }" >
2025-08-14 21:48:38 +01:00
< label for = "additional-information" class = "flex flex-col gap-1" >
< span class = "text-lg font-semibold text-contrast" >
Summary
< span class = "text-brand-red" > * < / span >
< / span >
< span > A sentence or two that describes your organization . < / span >
< / label >
< div class = "textarea-wrapper" >
2025-09-25 18:55:57 -07:00
< textarea
id = "additional-information"
v - model = "description"
: disabled = "disabled"
maxlength = "256"
/ >
2025-08-14 21:48:38 +01:00
< / div >
< / div >
2025-09-25 18:55:57 -07:00
< p class = "m-0 max-w-[30rem]" : class = "{ 'opacity-50': disabled }" >
2025-08-14 21:48:38 +01:00
You will be the owner of this organization , but you can invite other members and transfer
ownership at any time .
< / p >
< div class = "flex gap-2" >
< ButtonStyled color = "brand" >
2025-09-25 18:55:57 -07:00
< button :disabled = "disabled" @click ="createOrganization" >
2025-08-14 21:48:38 +01:00
< PlusIcon aria -hidden = " true " / >
Create organization
< / button >
< / ButtonStyled >
< ButtonStyled >
< button @click ="hide" >
< XIcon aria -hidden = " true " / >
Cancel
< / button >
< / ButtonStyled >
< / div >
< / div >
< / NewModal >
2024-01-06 15:09:26 -08:00
< / template >
2025-08-13 21:48:52 +01:00
< script setup lang = "ts" >
2025-08-14 21:48:38 +01:00
import { PlusIcon , XIcon } from '@modrinth/assets'
2025-09-25 18:55:57 -07:00
import { Admonition , ButtonStyled , injectNotificationManager , NewModal } from '@modrinth/ui'
2025-08-14 21:48:38 +01:00
import { ref } from 'vue'
2024-01-06 15:09:26 -08:00
2025-08-14 21:48:38 +01:00
const router = useNativeRouter ( )
const { addNotification } = injectNotificationManager ( )
2024-01-06 15:09:26 -08:00
2025-08-14 21:48:38 +01:00
const name = ref < string > ( '' )
const slug = ref < string > ( '' )
const description = ref < string > ( '' )
const manualSlug = ref < boolean > ( false )
const modal = ref < InstanceType < typeof NewModal > > ( )
2024-01-06 15:09:26 -08:00
2025-09-25 18:55:57 -07:00
const disabled = ref ( true )
2025-08-13 21:48:52 +01:00
async function createOrganization ( ) : Promise < void > {
2025-08-14 21:48:38 +01:00
startLoading ( )
try {
const value = {
name : name . value . trim ( ) ,
description : description . value . trim ( ) ,
slug : slug . value . trim ( ) . replace ( / +/g , '' ) ,
}
const result : any = await useBaseFetch ( 'organization' , {
method : 'POST' ,
body : JSON . stringify ( value ) ,
apiVersion : 3 ,
} )
modal . value ? . hide ( )
await router . push ( ` /organization/ ${ result . slug } ` )
} catch ( err : any ) {
console . error ( err )
addNotification ( {
title : 'An error occurred' ,
text : err . data ? err . data . description : err ,
type : 'error' ,
} )
}
stopLoading ( )
2024-01-06 15:09:26 -08:00
}
2025-08-13 21:48:52 +01:00
function show ( event ? : MouseEvent ) : void {
2025-08-14 21:48:38 +01:00
name . value = ''
description . value = ''
modal . value ? . show ( event )
2024-01-06 15:09:26 -08:00
}
2025-08-13 21:48:52 +01:00
function hide ( ) : void {
2025-08-14 21:48:38 +01:00
modal . value ? . hide ( )
2025-08-13 21:48:52 +01:00
}
function updateSlug ( ) : void {
2025-08-14 21:48:38 +01:00
if ( ! manualSlug . value ) {
slug . value = name . value
. trim ( )
. toLowerCase ( )
. replaceAll ( ' ' , '-' )
. replaceAll ( /[^a-zA-Z0-9!@$()`.+,_"-]/g , '' )
. replaceAll ( /--+/gm , '-' )
}
2024-01-06 15:09:26 -08:00
}
2025-08-13 21:48:52 +01:00
function setManualSlug ( ) : void {
2025-08-14 21:48:38 +01:00
manualSlug . value = true
2025-08-13 21:48:52 +01:00
}
2024-01-06 15:09:26 -08:00
defineExpose ( {
2025-08-14 21:48:38 +01:00
show ,
} )
2024-01-06 15:09:26 -08:00
< / script >
< style scoped lang = "scss" >
. modal - creation {
2025-08-14 21:48:38 +01:00
input {
width : 20 rem ;
max - width : 100 % ;
}
. text - input - wrapper {
width : 100 % ;
}
textarea {
min - height : 5 rem ;
}
. input - group {
margin - top : var ( -- gap - md ) ;
}
2024-01-06 15:09:26 -08:00
}
< / style >