Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates Initiative FE #39

Merged
merged 4 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/strict-boolean-expressions": 0,
"@typescript-eslint/promise-function-async": 0,
"@typescript-eslint/prefer-function-type": 0,
"comma-dangle": "off",
"@typescript-eslint/comma-dangle": ["error", "always-multiline" ]
},
Expand Down
6 changes: 4 additions & 2 deletions frontend/components/ExplicitInputSwitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ interface Props {
offLabel: string
value: boolean
}
interface Emits {
(e: 'update:value', value: boolean): void
}
const props = defineProps<Props>()

const emit = defineEmits<(e: 'update:value', value: boolean) => void>()
const emit = defineEmits<Emits>()

const value = computed({
get: () => props.value,
Expand Down
3 changes: 1 addition & 2 deletions frontend/components/TitleBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ onMounted(() => {
<div class="relative flex flex-column gap-2 align-items-start">
<PVButton
icon="pi pi-angle-left"
class="p-button-secondary p-button-text md:hidden p-0 text-600 pr-2"
style="margin-left: -.25rem"
class="p-button-secondary p-button-text md:hidden p-0 text-600 pr-2 -ml-1"
label="Back"
@click="back"
/>
Expand Down
49 changes: 49 additions & 0 deletions frontend/components/form/EditorField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { useSlots } from 'vue'
import { type EditorField, isValid } from '@/lib/editor'

interface Props {
editorField: EditorField<any, keyof any>
helpText?: string
startHelpTextExpanded?: boolean
isLoading?: boolean
loadingLabel?: string
invalidLabel?: string
validLabel?: string
}
const props = withDefaults(defineProps<Props>(), {
helpText: undefined,
startHelpTextExpanded: false,
loading: false,
loadingLabel: 'Loading...',
invalidLabel: 'Needs Attention',
validLabel: '',
})
const slots = useSlots()

const helpTextSlotExists = computed(() => slots['help-text'] !== undefined)
const valid = computed(() => isValid(props.editorField))
const hasValidation = computed(() => (props.editorField.validation ?? []).length > 0)
</script>

<template>
<FormField
:label="props.editorField.label"
:help-text="props.helpText"
:start-help-text-expanded="props.startHelpTextExpanded"
:is-loading="props.loading"
:loading-label="props.loadingLabel"
:has-validation="hasValidation"
:is-valid="valid"
:invalid-label="props.invalidLabel"
:valid-label="props.validLabel"
>
<template
v-if="helpTextSlotExists"
#help-text
>
<slot name="help-text" />
</template>
<slot />
</FormField>
</template>
35 changes: 15 additions & 20 deletions frontend/components/form/Field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,26 @@ interface Props {
label: string
helpText?: string
startHelpTextExpanded?: boolean
required?: boolean
loading?: boolean
completed?: boolean
requiredLabel?: string
isLoading?: boolean
loadingLabel?: string
completedLabel?: string
hasValidation?: boolean
isValid?: boolean
invalidLabel?: string
validLabel?: string
}
const props = withDefaults(defineProps<Props>(), {
helpText: '',
startHelpTextExpanded: true,
required: false,
loading: false,
completed: false,
requiredLabel: 'Needs Attention',
isLoading: false,
loadingLabel: 'Loading...',
completedLabel: '',
hasValidation: false,
isValid: false,
invalidLabel: 'Needs Attention',
validLabel: '',
})
const slots = useSlots()

const helpTextExists = computed(() => props.helpText !== '' || slots['help-text'] !== undefined)
const invalidFieldName = computed(() => {
if (props.required && !props.completed) { return props.requiredLabel }
return undefined
})
defineExpose({ invalidFieldName })
</script>

<template>
Expand All @@ -38,12 +33,12 @@ defineExpose({ invalidFieldName })
:help-text="props.helpText"
:help-text-exists="helpTextExists"
:start-help-text-expanded="props.startHelpTextExpanded"
:required="props.required"
:loading="props.loading"
:completed="props.completed"
:required-label="props.requiredLabel"
:is-loading="props.isLoading"
:loading-label="props.loadingLabel"
:completed-label="props.completedLabel"
:has-validation="props.hasValidation"
:is-valid="props.isValid"
:invalid-label="props.invalidLabel"
:valid-label="props.validLabel"
>
<template #help-text>
<slot name="help-text" />
Expand Down
20 changes: 10 additions & 10 deletions frontend/components/form/FieldHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ interface Props {
helpText: string
startHelpTextExpanded: boolean
helpTextExists: boolean
required: boolean
loading: boolean
completed: boolean
requiredLabel: string
isLoading: boolean
loadingLabel: string
completedLabel: string
hasValidation: boolean
isValid: boolean
validLabel: string
invalidLabel: string
}
const props = defineProps<Props>()
const { helpTextExpanded: computedHTE } = useLocalStorage()
Expand All @@ -36,25 +36,25 @@ const helpTextTextClass = computed(() => helpTextExpanded.value ? 'mb-2' : 'h-0'
@click="() => helpTextExpanded = !helpTextExpanded"
/>
<div
v-if="props.required && !props.completed"
v-if="props.hasValidation && !props.isValid"
class="flex align-items-center gap-1 p-error"
>
<i
class="pi pi-circle"
/>
<span>{{ props.requiredLabel }}</span>
<span>{{ props.invalidLabel }}</span>
</div>
<div
v-if="props.required && props.completed"
v-if="props.hasValidation && props.isValid"
class=" flex align-items-center gap-1 text-success"
>
<i
class="pi pi-check-circle"
/>
<span>{{ props.completedLabel }}</span>
<span>{{ props.validLabel }}</span>
</div>
<div
v-if="props.loading"
v-if="props.isLoading"
class="flex align-items-center gap-1 text-700"
>
<i
Expand Down
84 changes: 38 additions & 46 deletions frontend/components/initiative/Editor.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script setup lang="ts">
import { type EditorInitiative, isComplete } from '@/lib/editor'
import { type EditorInitiative } from '@/lib/editor'

const props = defineProps<{
interface Props {
editorInitiative: EditorInitiative
}>()

const emit = defineEmits<(e: 'update:editorInitiative', ei: EditorInitiative) => void>()
}
interface Emits {
(e: 'update:editorInitiative', ei: EditorInitiative): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()

const model = computed({
get: () => props.editorInitiative,
Expand All @@ -15,105 +18,94 @@ const model = computed({

<template>
gbdubs marked this conversation as resolved.
Show resolved Hide resolved
<div>
<FormField
label="Initiative Name"
<FormEditorField
help-text="The name of the PACTA initiative."
:required="model.name.isRequired"
:completed="isComplete(model.name)"
:editor-field="model.name"
>
<PVInputText
v-model="model.name.currentValue"
/>
</FormField>
<FormField
label="Initiative ID"
</FormEditorField>
<FormEditorField
help-text="This is the immutable unique identifier for the initiative. It can only contain alphanumeric characters, underscores, and dashes. This value will be shown in URLs, but will typically not be user visible."
:required="model.id.isRequired"
:completed="isComplete(model.id)"
:editor-field="model.id"
>
<PVInputText
v-model="model.id.currentValue"
:disabled="!!model.id.originalValue"
/>
</FormField>
<FormField
label="Affiliation"
</FormEditorField>
<FormEditorField
help-text="An optional description of the organization or entity that is hosting this initiative."
:editor-field="model.affiliation"
>
<PVInputText
v-model="model.affiliation.currentValue"
/>
</FormField>
<FormField
label="Public Description"
</FormEditorField>
<FormEditorField
help-text="The description of the initiative that will be shown to the public. Newlines will be respected."
:required="model.publicDescription.isRequired"
:completed="isComplete(model.publicDescription)"
:editor-field="model.publicDescription"
>
<PVTextarea
v-model="model.publicDescription.currentValue"
auto-resize
/>
</FormField>
<FormField
label="Internal Description"
</FormEditorField>
<FormEditorField
help-text="The description of the initiative that will be shown to members of the inititiative. Newlines will be respected."
:editor-field="model.internalDescription"
>
<PVTextarea
v-model="model.internalDescription.currentValue"
auto-resize
/>
</FormField>
<FormField
label="Participation Mechanism"
</FormEditorField>
<FormEditorField
help-text="When disabled, anyone can join this initiative. When enabled, initiative administrators can mint invitation codes that they can share with folks to allow them to join the project."
:editor-field="model.requiresInvitationToJoin"
>
<ExplicitInputSwitch
v-model:value="model.requiresInvitationToJoin.currentValue"
on-label="Requires Invitation To Join"
off-label="Anyone Can Join"
/>
</FormField>
<FormField
label="Open To New Members"
</FormEditorField>
<FormEditorField
:editor-field="model.isAcceptingNewMembers"
help-text="When enabled, new members can join the project through the joining mechanism selected above."
>
<ExplicitInputSwitch
v-model:value="model.isAcceptingNewMembers.currentValue"
on-label="Accepting New Members"
off-label="Closed To New Members"
/>
</FormField>
<FormField
label="Open To New Portfolios"
</FormEditorField>
<FormEditorField
:editor-field="model.isAcceptingNewPortfolios"
help-text="When enabled, initiative members can add new portfolios to the initiative."
>
<ExplicitInputSwitch
v-model:value="model.isAcceptingNewPortfolios.currentValue"
on-label="Accepting New Portfolios"
off-label="Closed To New Portfolios"
/>
</FormField>
<FormField
ref="fields"
label="Language"
</FormEditorField>
<FormEditorField
:editor-field="model.language"
help-text="What language should reports have when they are generated for this initiative?"
:required="model.language.isRequired"
:completed="isComplete(model.language)"
>
<LanguageSelector
v-model:value="model.language.currentValue"
/>
</FormField>
<FormField
label="PACTA Version"
</FormEditorField>
<FormEditorField
help-text="What version of the PACTA algorithm should this initiative use to generate reports?"
:required="model.pactaVersion.isRequired"
:completed="isComplete(model.pactaVersion)"
:editor-field="model.pactaVersion"
>
<PactaversionSelector
v-model:value="model.pactaVersion.currentValue"
/>
</FormField>
</FormEditorField>
</div>
</template>
41 changes: 41 additions & 0 deletions frontend/components/language/Representation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script setup lang="ts">
import { LanguageOptions, type LanguageCode } from '@/lib/language'

interface Props {
code: LanguageCode
fullName?: boolean
}
const props = withDefaults(defineProps<Props>(), { fullName: true })

const language = computed(() => presentOrFileBug(LanguageOptions.find((l) => l.code === props.code)))
</script>

<template>
<div
class="flex align-items-center"
:class="props.fullName ? 'gap-3' : 'gap-2'"
>
<div class="flag-wrapper shadow-1">
<img :src="`/img/flags/${language.code}.svg`">
</div>
<span class="text-lg">{{ props.fullName ? language.label : language.code.toUpperCase() }}</span>
</div>
</template>

<style lang="scss" scoped>
.flag-wrapper {
width: 1rem;
height: 1rem;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;

img {
flex-shrink: 0;
min-width: 100%;
min-height: 100%
}
}
</style>
Loading