|
@@ -99,6 +99,71 @@
|
|
|
</div>
|
|
</div>
|
|
|
</section>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
+ <section v-if="hasAbout" id="sobre-nos" class=" py-16 lg:py-24">
|
|
|
|
|
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="aboutCards.length" class="reveal text-center mb-12 lg:mb-16">
|
|
|
|
|
+ <h2 class="text-h4 text-text" style="font-size: clamp(1.5rem, 3vw, 2.125rem);">
|
|
|
|
|
+ Sobre Nós
|
|
|
|
|
+ </h2>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="aboutCards.length" class="grid grid-cols-1 gap-6">
|
|
|
|
|
+
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(card, index) in aboutCards"
|
|
|
|
|
+ :key="card.title"
|
|
|
|
|
+ :class="`reveal reveal-delay-${index + 1}`"
|
|
|
|
|
+ class="benefit-card"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="benefit-icon-wrap">
|
|
|
|
|
+ <i :class="`mdi ${card.icon} text-violet-normal`" style="font-size: 1.5rem;" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <h3 class="text-subtitle-1 font-semibold text-text mt-4 mb-2">
|
|
|
|
|
+ {{ card.title }}
|
|
|
|
|
+ </h3>
|
|
|
|
|
+ <p
|
|
|
|
|
+ v-for="(paragraph, paragraphIndex) in card.paragraphs"
|
|
|
|
|
+ :key="paragraphIndex"
|
|
|
|
|
+ lang="pt-BR"
|
|
|
|
|
+ class="text-body-2 text-text-2 text-justify hyphens-auto about-text"
|
|
|
|
|
+ :class="{ 'mt-3': paragraphIndex > 0 }"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ paragraph }}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-if="settings.about_history">
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="reveal text-center mb-10 lg:mb-12"
|
|
|
|
|
+ :class="{ 'mt-16 lg:mt-24': aboutCards.length }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <h2 class="text-h4 text-text" style="font-size: clamp(1.5rem, 3vw, 2.125rem);">
|
|
|
|
|
+ História
|
|
|
|
|
+ </h2>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="reveal reveal-delay-1 benefit-card history-card">
|
|
|
|
|
+ <div class="benefit-icon-wrap">
|
|
|
|
|
+ <i class="mdi mdi-book-open-page-variant-outline text-violet-normal" style="font-size: 1.5rem;" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p
|
|
|
|
|
+ v-for="(paragraph, index) in historyParagraphs"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ lang="pt-BR"
|
|
|
|
|
+ class="text-body-1 text-text-2 text-justify hyphens-auto history-text"
|
|
|
|
|
+ :class="index === 0 ? 'mt-5' : 'mt-4'"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ paragraph }}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
<section id="associe-se" class="bg-violet-light py-16 lg:py-24">
|
|
<section id="associe-se" class="bg-violet-light py-16 lg:py-24">
|
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
|
|
|
@@ -122,13 +187,26 @@
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-5 mb-6">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-5 mb-6">
|
|
|
|
|
|
|
|
<div class="field-group">
|
|
<div class="field-group">
|
|
|
- <label class="field-label">Nome Completo</label>
|
|
|
|
|
- <div class="field-input-wrap">
|
|
|
|
|
|
|
+ <label class="field-label">Nome</label>
|
|
|
|
|
+ <div :class="['field-input-wrap', { 'field-error': errors.nome }]">
|
|
|
|
|
+ <span class="material-icons field-icon">person</span>
|
|
|
|
|
+ <input
|
|
|
|
|
+ v-model="form.nome"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ placeholder="Seu nome"
|
|
|
|
|
+ class="field-input"
|
|
|
|
|
+ >
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="field-group">
|
|
|
|
|
+ <label class="field-label">Sobrenome</label>
|
|
|
|
|
+ <div :class="['field-input-wrap', { 'field-error': errors.sobrenome }]">
|
|
|
<span class="material-icons field-icon">person</span>
|
|
<span class="material-icons field-icon">person</span>
|
|
|
<input
|
|
<input
|
|
|
- v-model="form.nomeCompleto"
|
|
|
|
|
|
|
+ v-model="form.sobrenome"
|
|
|
type="text"
|
|
type="text"
|
|
|
- placeholder="Seu Nome"
|
|
|
|
|
|
|
+ placeholder="Seu sobrenome"
|
|
|
class="field-input"
|
|
class="field-input"
|
|
|
>
|
|
>
|
|
|
</div>
|
|
</div>
|
|
@@ -152,7 +230,7 @@
|
|
|
|
|
|
|
|
<div class="field-group">
|
|
<div class="field-group">
|
|
|
<label class="field-label">Crachá</label>
|
|
<label class="field-label">Crachá</label>
|
|
|
- <div class="field-input-wrap">
|
|
|
|
|
|
|
+ <div :class="['field-input-wrap', { 'field-error': errors.cracha }]">
|
|
|
<span class="material-icons field-icon">credit_card</span>
|
|
<span class="material-icons field-icon">credit_card</span>
|
|
|
<input
|
|
<input
|
|
|
v-model="form.cracha"
|
|
v-model="form.cracha"
|
|
@@ -195,7 +273,7 @@
|
|
|
|
|
|
|
|
<div class="field-group">
|
|
<div class="field-group">
|
|
|
<label class="field-label">Cargo</label>
|
|
<label class="field-label">Cargo</label>
|
|
|
- <div class="field-input-wrap">
|
|
|
|
|
|
|
+ <div :class="['field-input-wrap', { 'field-error': errors.cargoId }]">
|
|
|
<span class="material-icons field-icon">work</span>
|
|
<span class="material-icons field-icon">work</span>
|
|
|
<select v-model="form.cargoId" class="field-input">
|
|
<select v-model="form.cargoId" class="field-input">
|
|
|
<option :value="null" disabled>Selecione seu cargo</option>
|
|
<option :value="null" disabled>Selecione seu cargo</option>
|
|
@@ -208,7 +286,7 @@
|
|
|
|
|
|
|
|
<div class="field-group">
|
|
<div class="field-group">
|
|
|
<label class="field-label">Setor</label>
|
|
<label class="field-label">Setor</label>
|
|
|
- <div class="field-input-wrap">
|
|
|
|
|
|
|
+ <div :class="['field-input-wrap', { 'field-error': errors.setorId }]">
|
|
|
<span class="material-icons field-icon">business</span>
|
|
<span class="material-icons field-icon">business</span>
|
|
|
<select v-model="form.setorId" class="field-input">
|
|
<select v-model="form.setorId" class="field-input">
|
|
|
<option :value="null" disabled>Selecione seu setor</option>
|
|
<option :value="null" disabled>Selecione seu setor</option>
|
|
@@ -236,10 +314,13 @@
|
|
|
<Transition name="toast">
|
|
<Transition name="toast">
|
|
|
<div
|
|
<div
|
|
|
v-if="showSuccess"
|
|
v-if="showSuccess"
|
|
|
- class="fixed bottom-6 right-6 z-50 bg-violet-normal text-white px-5 py-4 rounded-xl shadow-xl flex items-center gap-3"
|
|
|
|
|
|
|
+ class="fixed bottom-6 right-6 z-50 max-w-sm bg-violet-normal text-white px-5 py-4 rounded-xl shadow-xl flex items-start gap-3"
|
|
|
>
|
|
>
|
|
|
<span class="material-icons text-xl">check_circle</span>
|
|
<span class="material-icons text-xl">check_circle</span>
|
|
|
- <span class="text-sm font-medium">Solicitação enviada com sucesso!</span>
|
|
|
|
|
|
|
+ <span class="text-sm font-medium">
|
|
|
|
|
+ Solicitação enviada com sucesso! Assim que a administração aprovar seu cadastro,
|
|
|
|
|
+ use a opção "Meu Primeiro Acesso" no login para criar sua senha.
|
|
|
|
|
+ </span>
|
|
|
</div>
|
|
</div>
|
|
|
</Transition>
|
|
</Transition>
|
|
|
|
|
|
|
@@ -266,12 +347,57 @@ const { settings, benefits } = useCompanyData()
|
|
|
|
|
|
|
|
const heroBackground = computed(() => settings.value.hero_background_url ?? heroBgFallback)
|
|
const heroBackground = computed(() => settings.value.hero_background_url ?? heroBgFallback)
|
|
|
|
|
|
|
|
|
|
+const toParagraphs = (raw: string | null): string[] => {
|
|
|
|
|
+ const content = raw?.trim()
|
|
|
|
|
+ if (!content) return []
|
|
|
|
|
+
|
|
|
|
|
+ const paragraphs: string[] = []
|
|
|
|
|
+ let startNewParagraph = true
|
|
|
|
|
+
|
|
|
|
|
+ for (const line of content.split(/\r?\n/)) {
|
|
|
|
|
+ const text = line.trim()
|
|
|
|
|
+
|
|
|
|
|
+ if (!text) {
|
|
|
|
|
+ startNewParagraph = true
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const previous = paragraphs[paragraphs.length - 1]
|
|
|
|
|
+ const isWrappedLine = !!previous && previous.length >= 60 && !/[.!?:;]$/.test(previous)
|
|
|
|
|
+
|
|
|
|
|
+ if (!startNewParagraph && isWrappedLine) {
|
|
|
|
|
+ paragraphs[paragraphs.length - 1] = `${previous} ${text}`
|
|
|
|
|
+ } else {
|
|
|
|
|
+ paragraphs.push(text)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ startNewParagraph = false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return paragraphs
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const aboutCards = computed(() =>
|
|
|
|
|
+ [
|
|
|
|
|
+ { title: 'Visão', icon: 'mdi-eye-outline', text: settings.value.about_vision },
|
|
|
|
|
+ { title: 'Missão', icon: 'mdi-bullseye-arrow', text: settings.value.about_mission },
|
|
|
|
|
+ { title: 'Valores', icon: 'mdi-heart-outline', text: settings.value.about_values },
|
|
|
|
|
+ ]
|
|
|
|
|
+ .filter((card) => !!card.text?.trim())
|
|
|
|
|
+ .map((card) => ({ ...card, paragraphs: toParagraphs(card.text) }))
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const hasAbout = computed(() => aboutCards.value.length > 0 || !!settings.value.about_history?.trim())
|
|
|
|
|
+
|
|
|
|
|
+const historyParagraphs = computed(() => toParagraphs(settings.value.about_history))
|
|
|
|
|
+
|
|
|
const scrollTo = (id: string) => {
|
|
const scrollTo = (id: string) => {
|
|
|
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
|
|
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const form = reactive({
|
|
const form = reactive({
|
|
|
- nomeCompleto: '',
|
|
|
|
|
|
|
+ nome: '',
|
|
|
|
|
+ sobrenome: '',
|
|
|
cpf: '',
|
|
cpf: '',
|
|
|
cracha: '',
|
|
cracha: '',
|
|
|
email: '',
|
|
email: '',
|
|
@@ -311,7 +437,33 @@ const formatPhone = (value: string): string =>
|
|
|
.replace(/(\d{5})(\d{1,4})$/, '$1-$2')
|
|
.replace(/(\d{5})(\d{1,4})$/, '$1-$2')
|
|
|
.slice(0, 15)
|
|
.slice(0, 15)
|
|
|
|
|
|
|
|
-const errors = reactive({ cpf: '', email: '', telefone: '' })
|
|
|
|
|
|
|
+const errors = reactive({
|
|
|
|
|
+ nome: '',
|
|
|
|
|
+ sobrenome: '',
|
|
|
|
|
+ cpf: '',
|
|
|
|
|
+ cracha: '',
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ telefone: '',
|
|
|
|
|
+ cargoId: '',
|
|
|
|
|
+ setorId: '',
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const clearErrors = () => {
|
|
|
|
|
+ Object.keys(errors).forEach((key) => { errors[key as keyof typeof errors] = '' })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const validateRequired = (): boolean => {
|
|
|
|
|
+ errors.nome = form.nome.trim() ? '' : 'Campo obrigatório'
|
|
|
|
|
+ errors.sobrenome = form.sobrenome.trim() ? '' : 'Campo obrigatório'
|
|
|
|
|
+ errors.cracha = form.cracha.trim() ? '' : 'Campo obrigatório'
|
|
|
|
|
+ errors.cargoId = form.cargoId ? '' : 'Campo obrigatório'
|
|
|
|
|
+ errors.setorId = form.setorId ? '' : 'Campo obrigatório'
|
|
|
|
|
+ errors.cpf = form.cpf.trim() ? errors.cpf : 'Campo obrigatório'
|
|
|
|
|
+ errors.email = form.email.trim() ? errors.email : 'Campo obrigatório'
|
|
|
|
|
+ errors.telefone = form.telefone.trim() ? errors.telefone : 'Campo obrigatório'
|
|
|
|
|
+
|
|
|
|
|
+ return !Object.values(errors).some(Boolean)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const handleCPF = (e: Event) => {
|
|
const handleCPF = (e: Event) => {
|
|
|
form.cpf = formatCPF((e.target as HTMLInputElement).value)
|
|
form.cpf = formatCPF((e.target as HTMLInputElement).value)
|
|
@@ -336,6 +488,11 @@ const onPhoneBlur = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
watch(() => form.email, () => { if (errors.email) errors.email = '' })
|
|
watch(() => form.email, () => { if (errors.email) errors.email = '' })
|
|
|
|
|
+watch(() => form.nome, () => { if (errors.nome) errors.nome = '' })
|
|
|
|
|
+watch(() => form.sobrenome, () => { if (errors.sobrenome) errors.sobrenome = '' })
|
|
|
|
|
+watch(() => form.cracha, () => { if (errors.cracha) errors.cracha = '' })
|
|
|
|
|
+watch(() => form.cargoId, () => { if (errors.cargoId) errors.cargoId = '' })
|
|
|
|
|
+watch(() => form.setorId, () => { if (errors.setorId) errors.setorId = '' })
|
|
|
|
|
|
|
|
const { registerLandingUser, positions, sectors } = useRegisterApi()
|
|
const { registerLandingUser, positions, sectors } = useRegisterApi()
|
|
|
|
|
|
|
@@ -346,7 +503,8 @@ const errorMessage = ref('')
|
|
|
|
|
|
|
|
const resetForm = () => {
|
|
const resetForm = () => {
|
|
|
Object.assign(form, {
|
|
Object.assign(form, {
|
|
|
- nomeCompleto: '',
|
|
|
|
|
|
|
+ nome: '',
|
|
|
|
|
+ sobrenome: '',
|
|
|
cpf: '',
|
|
cpf: '',
|
|
|
cracha: '',
|
|
cracha: '',
|
|
|
email: '',
|
|
email: '',
|
|
@@ -354,21 +512,22 @@ const resetForm = () => {
|
|
|
cargoId: null,
|
|
cargoId: null,
|
|
|
setorId: null,
|
|
setorId: null,
|
|
|
})
|
|
})
|
|
|
- Object.assign(errors, { cpf: '', email: '', telefone: '' })
|
|
|
|
|
|
|
+ clearErrors()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
const handleSubmit = async () => {
|
|
|
errors.cpf = form.cpf && !validateCPF(form.cpf) ? 'CPF inválido' : ''
|
|
errors.cpf = form.cpf && !validateCPF(form.cpf) ? 'CPF inválido' : ''
|
|
|
errors.email = form.email && !validateEmail(form.email) ? 'E-mail inválido' : ''
|
|
errors.email = form.email && !validateEmail(form.email) ? 'E-mail inválido' : ''
|
|
|
errors.telefone = form.telefone && form.telefone.replace(/\D/g, '').length < 10 ? 'Telefone inválido' : ''
|
|
errors.telefone = form.telefone && form.telefone.replace(/\D/g, '').length < 10 ? 'Telefone inválido' : ''
|
|
|
- if (errors.cpf || errors.email || errors.telefone) return
|
|
|
|
|
|
|
+ if (!validateRequired()) return
|
|
|
|
|
|
|
|
isLoading.value = true
|
|
isLoading.value = true
|
|
|
showError.value = false
|
|
showError.value = false
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
await registerLandingUser({
|
|
await registerLandingUser({
|
|
|
- name: form.nomeCompleto,
|
|
|
|
|
|
|
+ name: form.nome,
|
|
|
|
|
+ last_name: form.sobrenome,
|
|
|
cpf: form.cpf,
|
|
cpf: form.cpf,
|
|
|
registration: form.cracha,
|
|
registration: form.cracha,
|
|
|
email: form.email,
|
|
email: form.email,
|
|
@@ -378,7 +537,7 @@ const handleSubmit = async () => {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
showSuccess.value = true
|
|
showSuccess.value = true
|
|
|
- setTimeout(() => { showSuccess.value = false }, 4000)
|
|
|
|
|
|
|
+ setTimeout(() => { showSuccess.value = false }, 9000)
|
|
|
resetForm()
|
|
resetForm()
|
|
|
} catch (err: unknown) {
|
|
} catch (err: unknown) {
|
|
|
const error = err as { data?: { errors?: Record<string, string[]>; message?: string } }
|
|
const error = err as { data?: { errors?: Record<string, string[]>; message?: string } }
|
|
@@ -465,6 +624,24 @@ onMounted(() => {
|
|
|
box-shadow: 0 6px 24px rgba(102, 29, 117, 0.13);
|
|
box-shadow: 0 6px 24px rgba(102, 29, 117, 0.13);
|
|
|
transform: translateY(-2px);
|
|
transform: translateY(-2px);
|
|
|
}
|
|
}
|
|
|
|
|
+.history-card {
|
|
|
|
|
+ padding: 2rem 1.75rem;
|
|
|
|
|
+}
|
|
|
|
|
+@media (min-width: 1024px) {
|
|
|
|
|
+ .history-card {
|
|
|
|
|
+ padding: 2.5rem 3rem;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.about-text {
|
|
|
|
|
+ line-height: 1.7;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.history-text {
|
|
|
|
|
+ font-size: clamp(0.9375rem, 1.05vw, 1.0625rem);
|
|
|
|
|
+ line-height: 1.85;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.benefit-icon-wrap {
|
|
.benefit-icon-wrap {
|
|
|
display: inline-flex;
|
|
display: inline-flex;
|
|
|
align-items: center;
|
|
align-items: center;
|