vue components refactoring

This commit is contained in:
Čarodej
2022-04-13 16:19:10 +02:00
parent 6a4bfa8bfe
commit 338f8664b7
251 changed files with 1068 additions and 1943 deletions

View File

@@ -0,0 +1,127 @@
<template>
<transition name="popup">
<div
v-if="isVisibleWrapper"
@click.self="closePopup"
class="fixed top-0 left-0 right-0 bottom-0 z-50 grid h-full overflow-y-auto p-10"
>
<div class="fixed top-0 bottom-0 left-0 right-0 z-10 m-auto w-full bg-white shadow-xl dark:bg-dark-foreground md:relative md:w-[490px] md:rounded-xl">
<div class="flex h-full -translate-y-7 transform items-center justify-center px-8 text-center md:translate-y-0">
<div>
<img src="https://twemoji.maxcdn.com/v/13.1.0/svg/1f627.svg" alt="" class="mx-auto mb-4 w-20 md:mt-6 min-h-[80px]" />
<h1 v-if="title" class="mb-2 text-2xl font-bold">
{{ title }}
</h1>
<p v-if="message" class="mb-4 text-sm overflow-anywhere">
{{ message }}
</p>
</div>
</div>
<PopupActions>
<ButtonBase @click.native="closePopup" :button-style="buttonStyle" class="w-full">
{{ button }}
</ButtonBase>
</PopupActions>
</div>
</div>
</transition>
</template>
<script>
import ButtonBase from '../UI/Buttons/ButtonBase'
import { events } from '../../bus'
import PopupActions from "./Components/PopupActions";
export default {
name: 'AlertPopup',
components: {
PopupActions,
ButtonBase,
},
data() {
return {
isVisibleWrapper: false,
buttonStyle: undefined,
message: undefined,
title: undefined,
button: undefined,
emoji: undefined,
}
},
methods: {
closePopup() {
events.$emit('popup:close')
},
},
mounted() {
// Show alert
events.$on('alert:open', (args) => {
this.isVisibleWrapper = true
this.title = args.title || undefined
this.message = args.message || undefined
this.button = this.$te('alerts.error_confirm') ? this.$t('alerts.error_confirm') : 'Thats horrible!'
this.emoji = '😢😢😢'
this.buttonStyle = 'danger'
if (args.emoji) {
this.emoji = args.emoji
}
if (args.buttonStyle) {
this.buttonStyle = args.buttonStyle
}
if (args.button) {
this.button = args.button
}
})
// Show alert
events.$on('success:open', (args) => {
this.isVisibleWrapper = true
this.title = args.title
this.message = args.message
this.button = this.$t('alerts.success_confirm')
this.emoji = '🥳🥳🥳'
this.buttonStyle = 'theme'
if (args.emoji) {
this.emoji = args.emoji
}
})
// Close popup
events.$on('popup:close', () => {
this.isVisibleWrapper = false
})
},
}
</script>
<style scoped lang="scss">
// Animations
.popup-enter-active {
animation: popup-in 0.35s 0.15s ease both;
}
.popup-leave-active {
animation: popup-in 0.15s ease reverse;
}
@keyframes popup-in {
0% {
opacity: 0;
transform: scale(0.7);
}
100% {
opacity: 1;
transform: scale(1);
}
}
</style>

View File

@@ -0,0 +1,11 @@
<template>
<div class="absolute bottom-0 left-0 right-0 flex items-center space-x-4 px-6 py-4 pb-6 md:relative">
<slot />
</div>
</template>
<script>
export default {
name: 'PopupActions',
}
</script>

View File

@@ -0,0 +1,15 @@
<template>
<div
:class="type"
class="absolute top-16 bottom-24 left-0 right-0 h-auto overflow-auto px-6 md:relative md:top-0 md:bottom-0"
>
<slot />
</div>
</template>
<script>
export default {
name: 'PopupContent',
props: ['type'],
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<div class="flex items-center justify-between px-6 pt-6 pb-6">
<div class="flex items-center">
<div class="mr-3">
<upload-cloud-icon v-if="icon === 'upload'" size="18" class="vue-feather text-theme" />
<corner-down-right-icon v-if="icon === 'move'" size="18" class="vue-feather text-theme" />
<share-icon v-if="icon === 'share'" size="18" class="vue-feather text-theme" />
<edit2-icon v-if="icon === 'edit'" size="18" class="vue-feather text-theme" />
<key-icon v-if="icon === 'key'" size="18" class="vue-feather text-theme" />
<users-icon v-if="icon === 'users'" size="18" class="vue-feather text-theme" />
<user-plus-icon v-if="icon === 'user-plus'" size="18" class="vue-feather text-theme" />
<credit-card-icon v-if="icon === 'credit-card'" size="18" class="vue-feather text-theme" />
<bell-icon v-if="icon === 'bell'" size="18" class="vue-feather text-theme" />
</div>
<b class="text-base font-bold">
{{ title }}
</b>
</div>
<div @click="closePopup" class="-m-3 cursor-pointer p-3">
<x-icon size="14" class="hover-text-theme vue-feather" />
</div>
</div>
</template>
<script>
import {
BellIcon,
UploadCloudIcon,
CreditCardIcon,
KeyIcon,
UserPlusIcon,
CornerDownRightIcon,
LinkIcon,
XIcon,
Edit2Icon,
ShareIcon,
UsersIcon,
} from 'vue-feather-icons'
import { events } from '../../../bus'
export default {
name: 'PopupHeader',
props: ['title', 'icon'],
components: {
BellIcon,
UploadCloudIcon,
CornerDownRightIcon,
CreditCardIcon,
UserPlusIcon,
UsersIcon,
ShareIcon,
Edit2Icon,
LinkIcon,
KeyIcon,
XIcon,
},
methods: {
closePopup() {
events.$emit('popup:close')
},
},
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<transition name="popup">
<div
v-if="isVisibleWrapper"
@click.self="closePopup"
class="popup fixed top-0 left-0 right-0 bottom-0 z-50 grid h-full overflow-y-auto p-10 lg:absolute"
>
<div
class="fixed top-0 bottom-0 left-0 right-0 z-10 m-auto w-full bg-white shadow-xl dark:bg-dark-foreground md:relative md:w-[490px] md:rounded-xl"
>
<slot />
</div>
</div>
</transition>
</template>
<script>
import { events } from '../../../bus'
export default {
name: 'PopupWrapper',
props: ['name'],
data() {
return {
isVisibleWrapper: false,
}
},
methods: {
closePopup() {
events.$emit('popup:close')
},
},
created() {
// Open called popup
events.$on('popup:open', ({ name }) => {
if (this.name === name) this.isVisibleWrapper = true
if (this.name !== name) this.isVisibleWrapper = false
})
// Open called popup
events.$on('confirm:open', ({ name }) => {
if (this.name === name) this.isVisibleWrapper = true
})
// Close popup
events.$on('popup:close', () => (this.isVisibleWrapper = false))
},
}
</script>
<style lang="scss" scoped>
.popup-leave-active {
animation: popup-slide-in 0.15s ease reverse;
}
@media only screen and (min-width: 960px) {
.popup-enter-active {
animation: popup-slide-in 0.25s 0.1s ease both;
}
@keyframes popup-slide-in {
0% {
opacity: 0;
transform: translateY(100px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
@media only screen and (max-width: 960px) {
.popup-enter-active {
animation: popup-slide-in 0.35s 0.15s ease both;
}
@keyframes popup-slide-in {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<PopupWrapper>
<div class="flex h-full -translate-y-7 transform items-center justify-center px-8 text-center md:translate-y-0">
<div>
<img src="https://twemoji.maxcdn.com/v/13.1.0/svg/1f914.svg" alt="" class="mx-auto mb-4 w-20 md:mt-6 min-h-[80px]" />
<h1 v-if="title" class="mb-2 text-2xl font-bold">
{{ title }}
</h1>
<p v-if="message" class="mb-4 text-sm">
{{ message }}
</p>
</div>
</div>
<PopupActions>
<ButtonBase @click.native="closePopup" button-style="secondary" class="w-full"
>{{ $t('cancel') }}
</ButtonBase>
<ButtonBase @click.native="confirm" :button-style="buttonColor" class="w-full"
>{{ $t('yes_iam_sure') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import ButtonBase from '../UI/Buttons/ButtonBase'
import { events } from '../../bus'
export default {
name: 'ConfirmPopup',
components: {
PopupWrapper,
PopupActions,
ButtonBase,
},
data() {
return {
confirmationData: [],
message: undefined,
title: undefined,
buttonColor: undefined,
}
},
methods: {
closePopup() {
events.$emit('popup:close')
},
confirm() {
// Close popup
events.$emit('popup:close')
// Confirmation popup
events.$emit('action:confirmed', this.confirmationData)
// Clear confirmation data
this.confirmationData = []
},
},
mounted() {
// Show confirm
events.$on('confirm:open', (args) => {
this.title = args.title
this.message = args.message
this.confirmationData = args.action
this.buttonColor = 'danger'
if (args.buttonColor) {
this.buttonColor = args.buttonColor
}
})
},
}
</script>

View File

@@ -0,0 +1,113 @@
<template>
<PopupWrapper name="create-folder">
<!--Title-->
<PopupHeader :title="$t('popup_create_folder.title')" icon="edit" />
<!--Content-->
<PopupContent>
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="createFolder" ref="createForm" v-slot="{ invalid }" tag="form">
<!--Set folder name-->
<ValidationProvider tag="div" mode="passive" name="Title" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('popup_create_folder.label')" :error="errors[0]">
<input
v-model="name"
:class="{ '!border-rose-600': errors[0] }"
type="text"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('popup_create_folder.placeholder')"
/>
</AppInputText>
</ValidationProvider>
<AppInputSwitch
:title="$t('emoji_as_an_icon')"
:description="$t('replace_icon_with_emoji')"
:is-last="!isEmoji"
>
<SwitchInput v-model="isEmoji" :state="isEmoji" />
</AppInputSwitch>
<!--Set emoji-->
<EmojiPicker v-if="isEmoji" v-model="emoji" :default-emoji="emoji" />
</ValidationObserver>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary"
>{{ $t('cancel') }}
</ButtonBase>
<ButtonBase class="w-full" @click.native="createFolder" button-style="theme"
>{{ $t('popup_create_folder.title') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import ThumbnailItem from '../UI/Entries/ThumbnailItem'
import ButtonBase from '../UI/Buttons/ButtonBase'
import { required } from 'vee-validate/dist/rules'
import AppInputSwitch from '../Forms/Layouts/AppInputSwitch'
import AppInputText from '../Forms/Layouts/AppInputText'
import SwitchInput from '../Inputs/SwitchInput'
import { events } from '../../bus'
import EmojiPicker from '../Emoji/EmojiPicker'
export default {
name: 'CreateFolderPopup',
components: {
AppInputSwitch,
SwitchInput,
EmojiPicker,
AppInputText,
ValidationProvider,
ValidationObserver,
ThumbnailItem,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
required,
},
data() {
return {
name: undefined,
isEmoji: false,
emoji: undefined,
}
},
methods: {
async createFolder() {
// Validate fields
const isValid = await this.$refs.createForm.validate()
if (!isValid) return
await this.$store.dispatch('createFolder', {
name: this.name,
emoji: this.emoji,
})
this.$closePopup()
this.name = undefined
this.isEmoji = false
this.emoji = undefined
},
},
mounted() {
events.$on('popup:open', ({ name }) => {
if (name === 'create-folder' && !this.$isMobile()) this.$nextTick(() => this.$refs.input.focus())
})
},
}
</script>

View File

@@ -0,0 +1,866 @@
<template>
<PopupWrapper name="create-language">
<!--Title-->
<PopupHeader :title="$t('create_language')" icon="edit" />
<!--Content-->
<PopupContent class="!overflow-initial">
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="createLanguage" ref="createForm" v-slot="{ invalid }" tag="form">
<ValidationProvider
tag="div"
mode="passive"
name="Language Locale"
rules="required"
v-slot="{ errors }"
>
<AppInputText :title="$t('select_locale')" :error="errors[0]">
<SelectInput
v-model="form.locale"
:options="locales"
:placeholder="$t('select_language_locale')"
:isError="errors[0]"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Language Name" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('locale_name')" :error="errors[0]" :is-last="true">
<input
v-model="form.name"
:class="{ '!border-rose-600': errors[0] }"
type="text"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('type_language_name')"
/>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="createLanguage"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
{{ $t('create_language') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import AppInputText from '../Forms/Layouts/AppInputText'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import SelectInput from '../Inputs/SelectInput'
import ButtonBase from '../UI/Buttons/ButtonBase'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../bus'
import axios from 'axios'
export default {
name: 'CreateLanguagePopup',
components: {
ValidationProvider,
ValidationObserver,
AppInputText,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
SelectInput,
ButtonBase,
required,
},
data() {
return {
form: {
name: undefined,
locale: undefined,
},
isLoading: false,
locales: [
{
value: 'ab',
label: 'Abkhaz',
},
{
value: 'aa',
label: 'Afar',
},
{
value: 'af',
label: 'Afrikaans',
},
{
value: 'ak',
label: 'Akan',
},
{
value: 'sq',
label: 'Albanian',
},
{
value: 'am',
label: 'Amharic',
},
{
value: 'ar',
label: 'Arabic',
},
{
value: 'an',
label: 'Aragonese',
},
{
value: 'hy',
label: 'Armenian',
},
{
value: 'as',
label: 'Assamese',
},
{
value: 'av',
label: 'Avaric',
},
{
value: 'ae',
label: 'Avestan',
},
{
value: 'ay',
label: 'Aymara',
},
{
value: 'az',
label: 'Azerbaijani',
},
{
value: 'bm',
label: 'Bambara',
},
{
value: 'ba',
label: 'Bashkir',
},
{
value: 'eu',
label: 'Basque',
},
{
value: 'be',
label: 'Belarusian',
},
{
value: 'bn',
label: 'Bengali; Bangla',
},
{
value: 'bh',
label: 'Bihari',
},
{
value: 'bi',
label: 'Bislama',
},
{
value: 'bs',
label: 'Bosnian',
},
{
value: 'br',
label: 'Breton',
},
{
value: 'bg',
label: 'Bulgarian',
},
{
value: 'my',
label: 'Burmese',
},
{
value: 'ca',
label: 'Catalan; Valencian',
},
{
value: 'ch',
label: 'Chamorro',
},
{
value: 'ce',
label: 'Chechen',
},
{
value: 'ny',
label: 'Chichewa; Chewa; Nyanja',
},
{
value: 'zh',
label: 'Chinese',
},
{
value: 'cv',
label: 'Chuvash',
},
{
value: 'kw',
label: 'Cornish',
},
{
value: 'co',
label: 'Corsican',
},
{
value: 'cr',
label: 'Cree',
},
{
value: 'hr',
label: 'Croatian',
},
{
value: 'cs',
label: 'Czech',
},
{
value: 'da',
label: 'Danish',
},
{
value: 'dv',
label: 'Divehi; Dhivehi; Maldivian;',
},
{
value: 'nl',
label: 'Dutch',
},
{
value: 'dz',
label: 'Dzongkha',
},
{
value: 'en',
label: 'English',
},
{
value: 'eo',
label: 'Esperanto',
},
{
value: 'et',
label: 'Estonian',
},
{
value: 'ee',
label: 'Ewe',
},
{
value: 'fo',
label: 'Faroese',
},
{
value: 'fj',
label: 'Fijian',
},
{
value: 'fi',
label: 'Finnish',
},
{
value: 'fr',
label: 'French',
},
{
value: 'ff',
label: 'Fula; Fulah; Pulaar; Pular',
},
{
value: 'gl',
label: 'Galician',
},
{
value: 'lg',
label: 'Ganda',
},
{
value: 'ka',
label: 'Georgian',
},
{
value: 'de',
label: 'German',
},
{
value: 'el',
label: 'Greek, Modern',
},
{
value: 'gn',
label: 'Guaraní',
},
{
value: 'gu',
label: 'Gujarati',
},
{
value: 'ht',
label: 'Haitian; Haitian Creole',
},
{
value: 'ha',
label: 'Hausa',
},
{
value: 'he',
label: 'Hebrew (modern)',
},
{
value: 'hz',
label: 'Herero',
},
{
value: 'hi',
label: 'Hindi',
},
{
value: 'ho',
label: 'Hiri Motu',
},
{
value: 'hu',
label: 'Hungarian',
},
{
value: 'ia',
label: 'Interlingua',
},
{
value: 'id',
label: 'Indonesian',
},
{
value: 'ie',
label: 'Interlingue',
},
{
value: 'ga',
label: 'Irish',
},
{
value: 'ig',
label: 'Igbo',
},
{
value: 'ik',
label: 'Inupiaq',
},
{
value: 'io',
label: 'Ido',
},
{
value: 'is',
label: 'Icelandic',
},
{
value: 'it',
label: 'Italian',
},
{
value: 'iu',
label: 'Inuktitut',
},
{
value: 'ja',
label: 'Japanese',
},
{
value: 'jv',
label: 'Javanese',
},
{
value: 'kl',
label: 'Kalaallisut, Greenlandic',
},
{
value: 'kn',
label: 'Kannada',
},
{
value: 'kr',
label: 'Kanuri',
},
{
value: 'ks',
label: 'Kashmiri',
},
{
value: 'kk',
label: 'Kazakh',
},
{
value: 'km',
label: 'Khmer',
},
{
value: 'ki',
label: 'Kikuyu, Gikuyu',
},
{
value: 'rw',
label: 'Kinyarwanda',
},
{
value: 'rn',
label: 'Kirundi',
},
{
value: 'ky',
label: 'Kyrgyz',
},
{
value: 'kv',
label: 'Komi',
},
{
value: 'kg',
label: 'Kongo',
},
{
value: 'ko',
label: 'Korean',
},
{
value: 'ku',
label: 'Kurdish',
},
{
value: 'kj',
label: 'Kwanyama, Kuanyama',
},
{
value: 'la',
label: 'Latin',
},
{
value: 'lb',
label: 'Luxembourgish, Letzeburgesch',
},
{
value: 'li',
label: 'Limburgish, Limburgan, Limburger',
},
{
value: 'ln',
label: 'Lingala',
},
{
value: 'lo',
label: 'Lao',
},
{
value: 'lt',
label: 'Lithuanian',
},
{
value: 'lu',
label: 'Luba-Katanga',
},
{
value: 'lv',
label: 'Latvian',
},
{
value: 'gv',
label: 'Manx',
},
{
value: 'mk',
label: 'Macedonian',
},
{
value: 'mg',
label: 'Malagasy',
},
{
value: 'ms',
label: 'Malay',
},
{
value: 'ml',
label: 'Malayalam',
},
{
value: 'mt',
label: 'Maltese',
},
{
value: 'mi',
label: 'MÄori',
},
{
value: 'mr',
label: 'Marathi',
},
{
value: 'mh',
label: 'Marshallese',
},
{
value: 'mn',
label: 'Mongolian',
},
{
value: 'na',
label: 'Nauru',
},
{
value: 'nv',
label: 'Navajo, Navaho',
},
{
value: 'nb',
label: 'Norwegian',
},
{
value: 'nd',
label: 'North Ndebele',
},
{
value: 'ne',
label: 'Nepali',
},
{
value: 'ng',
label: 'Ndonga',
},
{
value: 'nn',
label: 'Norwegian Nynorsk',
},
{
value: 'no',
label: 'Norwegian',
},
{
value: 'ii',
label: 'Nuosu',
},
{
value: 'oc',
label: 'Occitan',
},
{
value: 'oj',
label: 'Ojibwe, Ojibwa',
},
{
value: 'cu',
label: 'Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic',
},
{
value: 'om',
label: 'Oromo',
},
{
value: 'or',
label: 'Oriya',
},
{
value: 'os',
label: 'Ossetian, Ossetic',
},
{
value: 'pa',
label: 'Panjabi, Punjabi',
},
{
value: 'pi',
label: 'Pali',
},
{
value: 'fa',
label: 'Persian (Farsi)',
},
{
value: 'pl',
label: 'Polish',
},
{
value: 'ps',
label: 'Pashto, Pushto',
},
{
value: 'pt',
label: 'Portuguese',
},
{
value: 'qu',
label: 'Quechua',
},
{
value: 'rm',
label: 'Romansh',
},
{
value: 'ro',
label: 'Romanian',
},
{
value: 'ru',
label: 'Russian',
},
{
value: 'sa',
label: 'Sanskrit',
},
{
value: 'sc',
label: 'Sardinian',
},
{
value: 'sd',
label: 'Sindhi',
},
{
value: 'se',
label: 'Northern Sami',
},
{
value: 'sm',
label: 'Samoan',
},
{
value: 'sg',
label: 'Sango',
},
{
value: 'sr',
label: 'Serbian',
},
{
value: 'gd',
label: 'Scottish Gaelic',
},
{
value: 'sn',
label: 'Shona',
},
{
value: 'si',
label: 'Sinhala, Sinhalese',
},
{
value: 'sk',
label: 'Slovak',
},
{
value: 'sl',
label: 'Slovene',
},
{
value: 'so',
label: 'Somali',
},
{
value: 'st',
label: 'Southern Sotho',
},
{
value: 'az',
label: 'South Azerbaijani',
},
{
value: 'nr',
label: 'South Ndebele',
},
{
value: 'es',
label: 'Spanish; Castilian',
},
{
value: 'su',
label: 'Sundanese',
},
{
value: 'sw',
label: 'Swahili',
},
{
value: 'ss',
label: 'Swati',
},
{
value: 'sv',
label: 'Swedish',
},
{
value: 'ta',
label: 'Tamil',
},
{
value: 'te',
label: 'Telugu',
},
{
value: 'tg',
label: 'Tajik',
},
{
value: 'th',
label: 'Thai',
},
{
value: 'ti',
label: 'Tigrinya',
},
{
value: 'bo',
label: 'Tibetan Standard, Tibetan, Central',
},
{
value: 'tk',
label: 'Turkmen',
},
{
value: 'tl',
label: 'Tagalog',
},
{
value: 'tn',
label: 'Tswana',
},
{
value: 'to',
label: 'Tonga (Tonga Islands)',
},
{
value: 'tr',
label: 'Turkish',
},
{
value: 'ts',
label: 'Tsonga',
},
{
value: 'tt',
label: 'Tatar',
},
{
value: 'tw',
label: 'Twi',
},
{
value: 'ty',
label: 'Tahitian',
},
{
value: 'ug',
label: 'Uyghur, Uighur',
},
{
value: 'uk',
label: 'Ukrainian',
},
{
value: 'ur',
label: 'Urdu',
},
{
value: 'uz',
label: 'Uzbek',
},
{
value: 've',
label: 'Venda',
},
{
value: 'vi',
label: 'Vielabele',
},
{
value: 'vo',
label: 'Volapük',
},
{
value: 'wa',
label: 'Walloon',
},
{
value: 'cy',
label: 'Welsh',
},
{
value: 'wo',
label: 'Wolof',
},
{
value: 'fy',
label: 'Western Frisian',
},
{
value: 'xh',
label: 'Xhosa',
},
{
value: 'yi',
label: 'Yiddish',
},
{
value: 'yo',
label: 'Yoruba',
},
{
value: 'za',
label: 'Zhuang, Chuang',
},
{
value: 'zu',
label: 'Zulu',
},
],
}
},
methods: {
async createLanguage() {
// Validate fields
const isValid = await this.$refs.createForm.validate()
if (isValid) {
this.isLoading = true
axios
.post('/api/admin/languages', this.form)
.then((response) => {
events.$emit('reload:languages', response.data)
})
.catch(() => {
this.$isSomethingWrong()
})
.finally(() => {
this.form = {
name: undefined,
locale: undefined,
}
this.isLoading = false
this.$closePopup()
})
}
},
},
}
</script>

View File

@@ -0,0 +1,134 @@
<template>
<PopupWrapper name="create-personal-token">
<PopupHeader :title="$t('create_personal_token')" icon="key" />
<PopupContent>
<ValidationObserver
v-if="!token"
@submit.prevent="createTokenForm"
ref="createToken"
v-slot="{ invalid }"
tag="form"
>
<ValidationProvider tag="div" mode="passive" name="Token Name" rules="required|min:3" v-slot="{ errors }">
<AppInputText :title="$t('token_name')" :error="errors[0]" :is-last="true">
<input
v-model="name"
:class="{ '!border-rose-600': errors[0] }"
type="text"
ref="input"
class="focus-border-theme input-dark"
:placeholder="$t('popup_personal_token.plc')"
/>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
<AppInputText v-if="token" :title="$t('popup_personal_token.your_token')" :is-last="true">
<CopyInput size="small" :str="token['plainTextToken']" />
<InfoBox style="margin-bottom: 0; margin-top: 20px">
<p v-html="$t('popup_personal_token.copy_token')"></p>
</InfoBox>
</AppInputText>
</PopupContent>
<PopupActions v-if="!token">
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="createTokenForm"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
{{ $t('create_token') }}
</ButtonBase>
</PopupActions>
<PopupActions v-if="token">
<ButtonBase class="w-full" @click.native="$closePopup" button-style="theme">
{{ $t('awesome_iam_done') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import AppInputText from '../Forms/Layouts/AppInputText'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import CopyInput from '../Inputs/CopyInput'
import ButtonBase from '../UI/Buttons/ButtonBase'
import InfoBox from '../UI/Others/InfoBox'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../bus'
import axios from 'axios'
export default {
name: 'CreatePersonalTokenPopup',
components: {
ValidationProvider,
ValidationObserver,
AppInputText,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
CopyInput,
required,
InfoBox,
},
data() {
return {
isLoading: false,
name: undefined,
token: undefined,
}
},
methods: {
async createTokenForm() {
const isValid = await this.$refs.createToken.validate()
if (!isValid) return
this.isLoading = true
axios
.post('/api/user/tokens', {
name: this.name,
})
.then((response) => {
this.token = response.data
events.$emit('reload-personal-access-tokens')
})
.catch(() => this.$isSomethingWrong())
.finally(() => {
this.isLoading = false
this.name = undefined
})
},
},
created() {
events.$on('popup:close', () => this.token = undefined)
}
}
</script>
<style lang="scss" scoped>
@import '../../../sass/vuefilemanager/inapp-forms';
@import '../../../sass/vuefilemanager/forms';
.dark {
.info-box {
background: lighten($dark_mode_foreground, 3%);
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<PopupWrapper name="move">
<!--Title-->
<PopupHeader :title="$t('popup_move_item.title')" icon="move" />
<!--Content-->
<PopupContent v-if="pickedItem" class="h-full pb-6 lg:max-h-96 sm:overflow-y-auto md:pb-0">
<!--Show Spinner when loading folders-->
<Spinner v-if="isLoadingTree" />
<!--Folder tree-->
<div v-if="!isLoadingTree && navigation">
<ThumbnailItem v-if="clipboard.length === 1 || isSelectedItem" class="mb-5" :item="pickedItem" />
<TitlePreview
class="mb-4"
icon="check-square"
:title="$t('selected_multiple')"
:subtitle="clipboard.length + ' ' + $tc('items', clipboard.length)"
v-if="clipboard.length > 1 && !isSelectedItem"
/>
<TreeMenu
class="-mx-4"
:disabled-by-id="pickedItem"
:depth="1"
:nodes="items"
v-for="items in navigation"
:key="items.id"
/>
</div>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary"
>{{ $t('cancel') }}
</ButtonBase>
<ButtonBase class="w-full" @click.native="moveItem" :button-style="selectedFolder ? 'theme' : 'secondary'"
>{{ $t('popup_move_item.submit') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import TitlePreview from '../UI/Labels/TitlePreview'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import ThumbnailItem from '../UI/Entries/ThumbnailItem'
import ButtonBase from '../UI/Buttons/ButtonBase'
import Spinner from '../UI/Others/Spinner'
import TreeMenu from '../UI/Trees/TreeMenu'
import { isArray } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '../../bus'
export default {
name: 'MoveItemPopup',
components: {
ThumbnailItem,
TitlePreview,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
TreeMenu,
Spinner,
},
computed: {
...mapGetters(['navigation', 'clipboard']),
},
data() {
return {
selectedFolder: undefined,
pickedItem: undefined,
isLoadingTree: true,
isSelectedItem: false,
}
},
methods: {
moveItem() {
// Prevent empty submit
if (!this.selectedFolder) return
// Prevent to move items to the same parent
if (
isArray(this.selectedFolder) &&
this.clipboard.find((item) => item.parent_id === this.selectedFolder.id)
)
return
// Move item
this.$store.dispatch('moveItem', {
to_item: this.selectedFolder,
item: this.isSelectedItem ? this.pickedItem : undefined,
})
// Close popup
events.$emit('popup:close')
// If is mobile, close the selecting mod after done the move action
if (this.$isMobile())
this.$store.commit('DISABLE_MULTISELECT_MODE')
},
},
mounted() {
events.$on('pick-folder', (folder) => {
if (folder.id === this.pickedItem.data.id) {
this.selectedFolder = undefined
} else if (!folder.id && folder.location === 'base') {
this.selectedFolder = 'base'
} else {
this.selectedFolder = folder
}
})
// Show Move item popup
events.$on('popup:open', (args) => {
if (args.name !== 'move') return
// Show tree spinner
this.isLoadingTree = true
// Get folder tree and hide spinner
if (this.$isThisRoute(this.$route, ['SharedWithMe'])) {
this.$store.dispatch('getTeamFolderTree').then(() => {
this.isLoadingTree = false
})
} else {
this.$store.dispatch('getFolderTree').then(() => {
this.isLoadingTree = false
})
}
// Store picked item
if (!this.clipboard.includes(args.item[0])) {
this.pickedItem = args.item[0]
this.isSelectedItem = true
}
if (this.clipboard.includes(args.item[0])) {
this.pickedItem = this.clipboard[0]
this.isSelectedItem = false
}
})
// Close popup
events.$on('popup:close', () => {
// Clear selected folder
setTimeout(() => {
this.selectedFolder = undefined
}, 150)
})
},
}
</script>

View File

@@ -0,0 +1,134 @@
<template>
<transition name="popup">
<div class="popup" v-if="processingPopup">
<div class="popup-wrapper">
<div class="popup-content">
<div class="spinner-wrapper">
<Spinner />
</div>
<h1 class="title">{{ processingPopup.title }}</h1>
<p class="message">{{ processingPopup.message }}</p>
</div>
</div>
</div>
</transition>
</template>
<script>
import Spinner from '../UI/Others/Spinner'
import { mapGetters } from 'vuex'
export default {
name: 'ProcessingPopup',
components: {
Spinner,
},
computed: {
...mapGetters(['processingPopup']),
},
}
</script>
<style scoped lang="scss">
@import '../../../sass/vuefilemanager/variables';
@import '../../../sass/vuefilemanager/mixins';
.spinner-wrapper {
padding-bottom: 90px;
position: relative;
}
.popup {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
overflow: auto;
height: 100%;
}
.popup-wrapper {
z-index: 12;
position: absolute;
left: 0;
right: 0;
max-width: 480px;
top: 50%;
transform: translateY(-50%) scale(1);
margin: 0 auto;
padding: 20px;
box-shadow: $light_mode_popup_shadow;
border-radius: 8px;
text-align: center;
background: white;
}
.popup-content {
.title {
@include font-size(22);
font-weight: 700;
color: $text;
}
.message {
@include font-size(16);
color: #333;
margin-top: 5px;
}
}
@media only screen and (max-width: 690px) {
.popup-wrapper {
padding: 20px;
left: 15px;
right: 15px;
}
.popup-content {
.title {
@include font-size(19);
}
.message {
@include font-size(15);
}
}
}
.dark {
.popup-wrapper {
background: $dark_mode_foreground;
}
.popup-content {
.title {
color: $dark_mode_text_primary;
}
.message {
color: $dark_mode_text_secondary;
}
}
}
// Animations
.popup-enter-active {
animation: popup-in 0.35s 0.15s ease both;
}
.popup-leave-active {
animation: popup-in 0.15s ease reverse;
}
@keyframes popup-in {
0% {
opacity: 0;
transform: scale(0.7);
}
100% {
opacity: 1;
transform: scale(1);
}
}
</style>

View File

@@ -0,0 +1,175 @@
<template>
<PopupWrapper name="rename-item">
<!--Title-->
<PopupHeader :title="$t('popup_rename.title', { item: itemTypeTitle })" icon="edit" />
<!--Content-->
<PopupContent>
<!--Item Thumbnail-->
<ThumbnailItem class="mb-5" :item="pickedItem" :setFolderIcon="{ emoji: emoji, color: null }" />
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="changeName" ref="renameForm" v-slot="{ invalid }" tag="form">
<!--Update item name-->
<ValidationProvider tag="div" mode="passive" name="Name" rules="required" v-slot="{ errors }">
<AppInputText
:title="$t('popup_rename.label')"
:error="errors[0]"
:is-last="pickedItem.data.type !== 'folder'"
>
<div class="relative flex items-center">
<input
v-model="pickedItem.data.attributes.name"
:class="{ '!border-rose-600': errors[0] }"
ref="input"
type="text"
class="!pr-10 focus-border-theme input-dark"
:placeholder="$t('popup_rename.placeholder')"
/>
<div @click="pickedItem.data.attributes.name = ''" class="absolute right-0 p-4 cursor-pointer">
<x-icon class="hover-text-theme" size="14" />
</div>
</div>
</AppInputText>
</ValidationProvider>
<!--Emoji-->
<AppInputSwitch
v-if="pickedItem.data.type === 'folder'"
:title="$t('emoji_as_an_icon')"
:description="$t('replace_icon_with_emoji')"
:is-last="!isEmoji"
>
<SwitchInput v-model="isEmoji" :state="isEmoji" />
</AppInputSwitch>
<!--Set emoji-->
<EmojiPicker
v-if="pickedItem.data.type === 'folder' && isEmoji"
v-model="emoji"
:default-emoji="emoji"
/>
</ValidationObserver>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('cancel') }}
</ButtonBase>
<ButtonBase class="w-full" @click.native="changeName" button-style="theme">
{{ $t('popup_share_edit.save') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import ThumbnailItem from '../UI/Entries/ThumbnailItem'
import ButtonBase from '../UI/Buttons/ButtonBase'
import AppInputSwitch from '../Forms/Layouts/AppInputSwitch'
import AppInputText from '../Forms/Layouts/AppInputText'
import { required } from 'vee-validate/dist/rules'
import SwitchInput from '../Inputs/SwitchInput'
import EmojiPicker from '../Emoji/EmojiPicker'
import { XIcon } from 'vue-feather-icons'
import { events } from '../../bus'
export default {
name: 'RenameItemPopup',
components: {
ValidationProvider,
ValidationObserver,
EmojiPicker,
AppInputSwitch,
SwitchInput,
AppInputText,
ThumbnailItem,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
required,
XIcon,
},
computed: {
itemTypeTitle() {
return this.pickedItem && this.pickedItem.data.type === 'folder'
? this.$t('folder')
: this.$t('file')
},
},
watch: {
isEmoji(val) {
if (!val) {
events.$emit('setFolderIcon', { emoji: undefined })
this.emoji = undefined
} else {
events.$emit('setFolderIcon', { emoji: this.emoji })
}
},
emoji(val) {
events.$emit('setFolderIcon', {
emoji: val,
})
},
},
data() {
return {
pickedItem: undefined,
isEmoji: false,
emoji: undefined,
}
},
methods: {
changeName() {
if (this.pickedItem.data.attributes.name && this.pickedItem.data.attributes.name !== '') {
let item = {
id: this.pickedItem.data.id,
type: this.pickedItem.data.type,
name: this.pickedItem.data.attributes.name,
}
item['emoji'] = this.emoji || null
if (!this.isEmoji) item['emoji'] = null
// Rename item request
this.$store.dispatch('renameItem', item)
// Rename item in view
events.$emit('change:name', item)
this.$closePopup()
}
},
},
mounted() {
// Show popup
events.$on('popup:open', (args) => {
if (args.name !== 'rename-item') return
this.isEmoji = false
if (!this.$isMobile()) {
this.$nextTick(() => this.$refs.input.focus())
}
// Set default emoji if exist
if (args.item.data.attributes.emoji) {
this.isEmoji = true
this.emoji = args.item.data.attributes.emoji
}
// Store picked item
this.pickedItem = args.item
})
},
}
</script>

View File

@@ -0,0 +1,304 @@
<template>
<PopupWrapper name="share-create">
<!--Title-->
<PopupHeader :title="$t('popup_share_create.title', { item: itemTypeTitle })" icon="share" />
<!--Content-->
<PopupContent class="!overflow-initial">
<!--Item Thumbnail-->
<ThumbnailItem class="mb-5" :item="pickedItem" />
<!--Form to set sharing-->
<ValidationObserver
v-if="!isGeneratedShared"
@submit.prevent
ref="shareForm"
v-slot="{ invalid }"
tag="form"
>
<!--Permission Select-->
<ValidationProvider
v-if="isFolder"
tag="div"
mode="passive"
name="Permission"
rules="required"
v-slot="{ errors }"
>
<AppInputText :title="$t('permission')" :error="errors[0]">
<SelectInput
v-model="shareOptions.permission"
:options="$translateSelectOptions(permissionOptions)"
:placeholder="$t('shared_form.placeholder_permission')"
:isError="errors[0]"
/>
</AppInputText>
</ValidationProvider>
<!--Password Switch-->
<div>
<AppInputSwitch
:title="$t('password_protected')"
:description="$t('popup.share.password_description')"
>
<SwitchInput
v-model="shareOptions.isPassword"
class="switch"
:state="shareOptions.isPassword"
/>
</AppInputSwitch>
<!--Set password-->
<ValidationProvider
v-if="shareOptions.isPassword"
tag="div"
mode="passive"
name="Password"
rules="required"
v-slot="{ errors }"
>
<AppInputText :error="errors[0]" class="-mt-2">
<input
v-model="shareOptions.password"
:class="{ '!border-rose-600': errors[0] }"
type="text"
class="focus-border-theme input-dark"
:placeholder="$t('page_sign_in.placeholder_password')"
/>
</AppInputText>
</ValidationProvider>
</div>
<!--Expiration switch-->
<div>
<AppInputSwitch :title="$t('expiration')" :description="$t('popup.share.expiration_description')">
<SwitchInput v-model="isExpiration" class="switch" :state="isExpiration" />
</AppInputSwitch>
<!--Set expiration-->
<AppInputText v-if="isExpiration" class="-mt-2">
<SelectBoxInput
v-model="shareOptions.expiration"
:data="$translateSelectOptions(expirationList)"
class="box"
/>
</AppInputText>
</div>
<!--Send on emails switch-->
<div>
<AppInputSwitch
:title="$t('popup.share.email_send')"
:description="$t('popup.share.email_description')"
:is-last="!isEmailSharing"
>
<SwitchInput v-model="isEmailSharing" class="switch" :state="isEmailSharing" />
</AppInputSwitch>
<!--Emails-->
<ValidationProvider
v-if="isEmailSharing"
tag="div"
mode="passive"
name="Email"
rules="required"
v-slot="{ errors }"
class="-mt-2 mb-1"
>
<AppInputText :error="errors[0]" class="-mt-2" :is-last="true">
<MultiEmailInput
rules="required"
v-model="shareOptions.emails"
:label="$t('recipients')"
:is-error="errors[0]"
/>
</AppInputText>
</ValidationProvider>
</div>
</ValidationObserver>
<!--Copy generated link-->
<AppInputText v-if="isGeneratedShared" :title="$t('get_your_link')" :is-last="true">
<CopyShareLink :item="pickedItem" />
</AppInputText>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase v-if="!isGeneratedShared" class="w-full" @click.native="$closePopup()" button-style="secondary">
{{ $t('cancel') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="submitShareOptions"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
{{ submitButtonText }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import AppInputText from '../Forms/Layouts/AppInputText'
import AppInputSwitch from '../Forms/Layouts/AppInputSwitch'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import SelectBoxInput from '../Inputs/SelectBoxInput'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import MultiEmailInput from '../Inputs/MultiEmailInput'
import SwitchInput from '../Inputs/SwitchInput'
import SelectInput from '../Inputs/SelectInput'
import ThumbnailItem from '../UI/Entries/ThumbnailItem'
import ActionButton from '../UI/Buttons/ActionButton'
import CopyShareLink from '../Inputs/CopyShareLink'
import ButtonBase from '../UI/Buttons/ButtonBase'
import InfoBox from '../UI/Others/InfoBox'
import { LinkIcon, MailIcon } from 'vue-feather-icons'
import { required } from 'vee-validate/dist/rules'
import { mapGetters } from 'vuex'
import { events } from '../../bus'
import axios from 'axios'
export default {
name: 'ShareCreatePopup',
components: {
ValidationProvider,
ValidationObserver,
AppInputText,
AppInputSwitch,
SelectBoxInput,
ThumbnailItem,
ActionButton,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
MultiEmailInput,
SelectInput,
SwitchInput,
ButtonBase,
CopyShareLink,
MailIcon,
required,
LinkIcon,
InfoBox,
},
computed: {
...mapGetters(['permissionOptions', 'expirationList']),
itemTypeTitle() {
return this.pickedItem && this.pickedItem.data.type === 'folder'
? this.$t('folder')
: this.$t('file')
},
isFolder() {
return this.pickedItem && this.pickedItem.data.type === 'folder'
},
submitButtonText() {
return this.isGeneratedShared ? this.$t('awesome_iam_done') : this.$t('generate_link')
},
},
watch: {
isExpiration(val) {
if (!val) this.shareOptions.expiration = undefined
},
},
data() {
return {
isExpiration: false,
isEmailSharing: false,
shareOptions: {
isPassword: false,
expiration: undefined,
password: undefined,
permission: undefined,
type: undefined,
id: undefined,
emails: undefined,
},
pickedItem: undefined,
isGeneratedShared: false,
isLoading: false,
sharedViaEmail: false,
}
},
methods: {
async submitShareOptions() {
// If shared was generated, then close popup
if (this.isGeneratedShared) {
events.$emit('popup:close')
return
}
// Validate fields
const isValid = await this.$refs.shareForm.validate()
if (!isValid) return
this.isLoading = true
// Send request to get share link
axios
.post(`/api/share`, this.shareOptions)
.then((response) => {
// End loading
this.isGeneratedShared = true
this.$store.commit('UPDATE_SHARED_ITEM', response.data)
this.pickedItem.data.relationships.shared = response.data
})
.catch(() => {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
// End loading
this.isLoading = false
})
.finally(() => {
this.isLoading = false
})
},
},
mounted() {
events.$on('emailsInputValues', (emails) => (this.shareOptions.emails = emails))
// Show popup
events.$on('popup:open', (args) => {
if (args.name !== 'share-create') return
// Store picked item
this.pickedItem = args.item
this.shareOptions.type = args.item.data.type
this.shareOptions.id = args.item.data.id
})
// Close popup
events.$on('popup:close', () => {
// Restore data
setTimeout(() => {
this.isGeneratedShared = false
this.isExpiration = false
this.isEmailSharing = false
this.shareOptions = {
isPassword: false,
expiration: undefined,
password: undefined,
permission: undefined,
type: undefined,
id: undefined,
emails: undefined,
}
}, 150)
})
},
}
</script>

View File

@@ -0,0 +1,402 @@
<template>
<PopupWrapper name="share-edit">
<!--Title-->
<PopupHeader :title="popupTitle" icon="share" />
<!--Qr Code-->
<div v-if="pickedItem && activeSection === 'qr-code'">
<PopupContent class="flex items-center justify-center">
<div v-if="!qrCode" class="relative my-8">
<Spinner />
</div>
<div v-if="qrCode" v-html="qrCode" class="my-5 overflow-hidden rounded-xl"></div>
</PopupContent>
<PopupActions>
<ButtonBase class="w-full" @click.native="showSection(undefined)" button-style="secondary">
{{ $t('show_details') }}
</ButtonBase>
<ButtonBase class="w-full" @click.native="$closePopup()" button-style="theme">
{{ $t('awesome_iam_done') }}
</ButtonBase>
</PopupActions>
</div>
<!--Share via email-->
<div v-if="pickedItem && activeSection === 'email-sharing'">
<PopupContent>
<!--Item Thumbnail-->
<ThumbnailItem class="mb-4" :item="pickedItem" />
<ValidationObserver @submit.prevent v-slot="{ invalid }" ref="shareEmail" tag="form">
<ValidationProvider tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }">
<AppInputText title="Share with" :error="errors[0]" :is-last="true">
<MultiEmailInput
rules="required"
v-model="emails"
:label="$t('shared_form.label_send_to_recipients')"
:is-error="errors[0]"
/>
</AppInputText>
</ValidationProvider>
</ValidationObserver>
</PopupContent>
<PopupActions>
<ButtonBase class="w-full" @click.native="showSection(undefined)" button-style="secondary">
{{ $t('show_details') }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="sendViaEmail"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
{{ $t('send') }}
</ButtonBase>
</PopupActions>
</div>
<!--Update sharing-->
<div v-if="pickedItem && !activeSection">
<PopupContent class="!overflow-initial">
<!--Item Thumbnail-->
<ThumbnailItem class="mb-5" :item="pickedItem" />
<!--Get share link-->
<AppInputText :title="$t('get_your_link')">
<CopyShareLink :item="pickedItem" />
</AppInputText>
<ValidationObserver @submit.prevent ref="shareForm" v-slot="{ invalid }" tag="form">
<!--Permission Select-->
<ValidationProvider
v-if="isFolder"
tag="div"
mode="passive"
name="Permission"
rules="required"
v-slot="{ errors }"
>
<AppInputText :title="$t('permission')" :error="errors[0]">
<SelectInput
v-model="shareOptions.permission"
:options="$translateSelectOptions(permissionOptions)"
:default="shareOptions.permission"
:placeholder="$t('shared_form.placeholder_permission')"
:isError="errors[0]"
/>
</AppInputText>
</ValidationProvider>
<!--Password Switch-->
<div>
<AppInputSwitch
:title="$t('password_protected')"
:description="$t('popup.share.password_description')"
>
<SwitchInput
v-model="shareOptions.isProtected"
class="switch"
:state="shareOptions.isProtected"
/>
</AppInputSwitch>
<ActionButton
v-if="
pickedItem.data.relationships.shared.data.attributes.protected &&
canChangePassword &&
shareOptions.isProtected
"
@click.native="changePassword"
class="mb-6 -mt-4"
>
{{ $t('popup_share_edit.change_pass') }}
</ActionButton>
<!--Set password-->
<ValidationProvider
v-if="shareOptions.isProtected && !canChangePassword"
tag="div"
mode="passive"
name="Password"
rules="required"
v-slot="{ errors }"
>
<AppInputText :error="errors[0]" class="-mt-2">
<input
v-model="shareOptions.password"
:class="{ '!border-rose-600': errors[0] }"
type="text"
class="focus-border-theme input-dark"
:placeholder="$t('page_sign_in.placeholder_password')"
/>
</AppInputText>
</ValidationProvider>
</div>
<!--Expiration switch-->
<div>
<AppInputSwitch
:title="$t('expiration')"
:description="$t('popup.share.expiration_description')"
:is-last="!shareOptions.expiration"
>
<SwitchInput
v-model="shareOptions.expiration"
class="switch"
:state="shareOptions.expiration ? 1 : 0"
/>
</AppInputSwitch>
<!--Set expiration-->
<AppInputText v-if="shareOptions.expiration" class="-mt-2" :is-last="true">
<SelectBoxInput
v-model="shareOptions.expiration"
:data="$translateSelectOptions(expirationList)"
:value="shareOptions.expiration"
class="box"
/>
</AppInputText>
</div>
</ValidationObserver>
</PopupContent>
<PopupActions>
<ButtonBase
class="w-full"
@click.native="destroySharing"
:button-style="destroyButtonStyle"
:loading="isDeleting"
:disabled="isDeleting"
>
{{ destroyButtonText }}
</ButtonBase>
<ButtonBase
class="w-full"
@click.native="updateShareOptions"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>
{{ $t('store_changes') }}
</ButtonBase>
</PopupActions>
</div>
</PopupWrapper>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import MultiEmailInput from '../Inputs/MultiEmailInput'
import SelectBoxInput from '../Inputs/SelectBoxInput'
import CopyShareLink from '../Inputs/CopyShareLink'
import PopupWrapper from './Components/PopupWrapper'
import PopupActions from './Components/PopupActions'
import PopupContent from './Components/PopupContent'
import PopupHeader from './Components/PopupHeader'
import SwitchInput from '../Inputs/SwitchInput'
import SelectInput from '../Inputs/SelectInput'
import ThumbnailItem from '../UI/Entries/ThumbnailItem'
import ActionButton from '../UI/Buttons/ActionButton'
import ButtonBase from '../UI/Buttons/ButtonBase'
import AppInputSwitch from '../Forms/Layouts/AppInputSwitch'
import AppInputText from '../Forms/Layouts/AppInputText'
import { required } from 'vee-validate/dist/rules'
import Spinner from '../UI/Others/Spinner'
import { events } from '../../bus'
import { mapGetters } from 'vuex'
import axios from 'axios'
export default {
name: 'ShareEditPopup',
components: {
ValidationProvider,
ValidationObserver,
MultiEmailInput,
AppInputSwitch,
SelectBoxInput,
ThumbnailItem,
CopyShareLink,
ActionButton,
PopupWrapper,
PopupActions,
AppInputText,
PopupContent,
PopupHeader,
SelectInput,
SwitchInput,
ButtonBase,
required,
Spinner,
},
computed: {
...mapGetters(['permissionOptions', 'expirationList', 'user']),
popupTitle() {
return (
{
'qr-code': this.$t('get_qr_code'),
'email-sharing': this.$t('share_with_multiple_emails'),
}[this.activeSection] || this.$t('popup_share_edit.title')
)
},
isFolder() {
return this.pickedItem && this.pickedItem.data.type === 'folder'
},
destroyButtonText() {
return this.isConfirmedDestroy ? this.$t('popup_share_edit.confirm') : this.$t('popup_share_edit.stop')
},
destroyButtonStyle() {
return this.isConfirmedDestroy ? 'danger-solid' : 'secondary'
},
},
watch: {
'shareOptions.expiration': function (val) {
if (!val) {
this.shareOptions.expiration = undefined
}
},
},
data() {
return {
activeSection: undefined,
shareOptions: undefined,
pickedItem: undefined,
emails: undefined,
qrCode: undefined,
isConfirmedDestroy: false,
canChangePassword: false,
isMoreOptions: false,
isDeleting: false,
isLoading: false,
}
},
methods: {
getQrCode() {
axios
.get(`/api/share/${this.shareOptions.token}/qr`)
.then((response) => {
this.qrCode = response.data
})
.catch(() => this.$isSomethingWrong())
},
showSection(section = undefined) {
this.activeSection = section
},
changePassword() {
this.canChangePassword = false
},
async sendViaEmail() {
// Validate email field
const isValid = await this.$refs.shareEmail.validate()
if (!isValid) return
this.isLoading = true
axios
.post(`/api/share/${this.shareOptions.token}/email`, {
emails: this.emails,
})
.catch(() => {
this.$isSomethingWrong()
})
.finally(() => {
this.isLoading = false
this.$closePopup()
})
},
async destroySharing() {
// Set confirm button
if (!this.isConfirmedDestroy) {
this.isConfirmedDestroy = true
return
}
// Start deleting spinner button
this.isDeleting = true
// Send delete request
await this.$store
.dispatch('shareCancel', this.pickedItem)
.catch(() => {
// End deleting spinner button
this.isDeleting = false
})
.finally(() => this.$closePopup())
},
async updateShareOptions() {
// Validate fields
const isValid = await this.$refs.shareForm.validate()
if (!isValid) return
this.isLoading = true
// Send request to get share link
axios
.post('/api/share/' + this.shareOptions.token, {
permission: this.shareOptions.permission,
protected: this.shareOptions.isProtected,
expiration: this.shareOptions.expiration,
password: this.shareOptions.password ? this.shareOptions.password : undefined,
_method: 'patch',
})
.then((response) => {
// Update shared data
this.$store.commit('UPDATE_SHARED_ITEM', response.data)
events.$emit('popup:close')
})
.catch(() => {
this.$isSomethingWrong()
})
.finally(() => {
this.isLoading = false
})
},
},
mounted() {
events.$on('emailsInputValues', (emails) => (this.emails = emails))
// Show popup
events.$on('popup:open', (args) => {
if (args.name !== 'share-edit') return
// Store picked item
this.pickedItem = args.item
// Store shared options
this.shareOptions = {
id: args.item.data.relationships.shared.data.id,
token: args.item.data.relationships.shared.data.attributes.token,
expiration: args.item.data.relationships.shared.data.attributes.expire_in,
isProtected: args.item.data.relationships.shared.data.attributes.protected,
permission: args.item.data.relationships.shared.data.attributes.permission,
password: undefined,
}
if (args.section) this.activeSection = args.section
if (args.section === 'qr-code') this.getQrCode()
this.canChangePassword = args.item.data.relationships.shared.data.attributes.protected
})
events.$on('popup:close', () => {
// Reset data
setTimeout(() => {
this.isDeleting = false
this.isConfirmedDestroy = false
this.canChangePassword = false
this.shareOptions = undefined
this.pickedItem = undefined
this.activeSection = undefined
this.qrCode = undefined
}, 150)
})
},
}
</script>