finish file request

This commit is contained in:
Čarodej
2022-02-22 09:35:06 +01:00
parent 171ee5fa04
commit c3398f0da4
9 changed files with 185 additions and 113 deletions
@@ -4,15 +4,16 @@
<NavigationBar /> <NavigationBar />
<div class="flex items-center"> <div class="flex items-center">
<!--I am Done-->
<div class="bg-theme-200 mr-6 flex cursor-pointer items-center rounded-lg py-1 pr-1 pl-4"> <div class="bg-theme-200 mr-6 flex cursor-pointer items-center rounded-lg py-1 pr-1 pl-4">
<b @click="uploadingDone" class="text-theme mr-3 text-xs"> <b @click="uploadingDone" class="text-theme mr-3 text-xs">
{{ isDone ? $t('Awesome!') : $t('Tell Jane you are done!') }} {{ $t('Tell Jane you are done!') }}
</b> </b>
<img <MemberAvatar
class="w-8 rounded-lg" :member="uploadRequest.data.relationships.user"
src="http://192.168.1.112:8000/avatars/md-f45abbe5-962c-4229-aef2-9991e96d54d9.png" :size="34"
alt="Avatar" />
/>
</div> </div>
<!--Create button--> <!--Create button-->
@@ -89,6 +90,7 @@ import PopoverItem from '../Desktop/PopoverItem'
import UploadProgress from './UploadProgress' import UploadProgress from './UploadProgress'
import NavigationBar from './NavigationBar' import NavigationBar from './NavigationBar'
import ToolbarButton from './ToolbarButton' import ToolbarButton from './ToolbarButton'
import MemberAvatar from "./MemberAvatar"
import OptionUpload from './OptionUpload' import OptionUpload from './OptionUpload'
import OptionGroup from './OptionGroup' import OptionGroup from './OptionGroup'
import SearchBar from './SearchBar' import SearchBar from './SearchBar'
@@ -104,6 +106,7 @@ export default {
PopoverWrapper, PopoverWrapper,
NavigationBar, NavigationBar,
ToolbarButton, ToolbarButton,
MemberAvatar,
OptionUpload, OptionUpload,
OptionGroup, OptionGroup,
PopoverItem, PopoverItem,
@@ -111,30 +114,22 @@ export default {
Option, Option,
}, },
computed: { computed: {
...mapGetters(['isVisibleNavigationBars', 'currentTeamFolder', 'currentFolder', 'sharedDetail', 'clipboard']), ...mapGetters(['isVisibleNavigationBars', 'currentTeamFolder', 'currentFolder', 'sharedDetail', 'clipboard', 'uploadRequest']),
canEdit() {
return this.sharedDetail && this.sharedDetail.data.attributes.permission === 'editor'
},
canManipulate() { canManipulate() {
return this.clipboard[0] return this.clipboard[0]
}, },
}, },
data() {
return {
isDone: false,
}
},
methods: { methods: {
uploadingDone() { uploadingDone() {
// TODO: add name to the message // TODO: add name to the message
if (!this.isDone) { events.$emit('confirm:open', {
events.$emit('toaster', { title: this.$t('Are you sure you uploaded all files you want for {name}?', {name: this.uploadRequest.relationships.user.data.attributes.name}),
type: 'success', message: this.$t("You won't be able to upload any files here once again."),
message: this.$t('We notified Jane about your new uploads successfully.'), action: {
}) id: this.$router.currentRoute.params.token,
} operation: 'close-upload-request',
},
this.isDone = true })
}, },
showCreateMenu() { showCreateMenu() {
events.$emit('popover:open', 'desktop-create') events.$emit('popover:open', 'desktop-create')
+1 -1
View File
@@ -146,7 +146,7 @@ const actions = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Get route // Get route
let route = { let route = {
RequestUpload: `/api/upload-request/${router.currentRoute.params.token}`, RequestUpload: `/api/upload-request/${router.currentRoute.params.token}/upload`,
Public: `/api/editor/upload/${router.currentRoute.params.token}`, Public: `/api/editor/upload/${router.currentRoute.params.token}`,
}[router.currentRoute.name] || '/api/upload' }[router.currentRoute.name] || '/api/upload'
+10 -2
View File
@@ -9,8 +9,7 @@ const defaultState = {
} }
const actions = { const actions = {
getUploadRequestDetail: ({ commit, getters }) => { getUploadRequestDetail: ({ commit }) => {
axios.get(`/api/upload-request/${router.currentRoute.params.token}`) axios.get(`/api/upload-request/${router.currentRoute.params.token}`)
.then((response) => { .then((response) => {
@@ -24,6 +23,15 @@ const actions = {
} }
}) })
}, },
closeUploadRequest: ({ commit }) => {
axios
.delete(`/api/upload-request/${router.currentRoute.params.token}`)
.then((response) => {
commit('LOADING_STATE', { loading: false, data: [] })
commit('SET_UPLOAD_REQUEST', response.data)
})
.catch(() => this.$isSomethingWrong())
},
} }
const mutations = { const mutations = {
@@ -15,29 +15,29 @@
</OptionGroup> </OptionGroup>
<OptionGroup :title="$t('Create')"> <OptionGroup :title="$t('Create')">
<Option <Option
@click.stop.native="createFolder" @click.stop.native="createFolder"
:title="$t('actions.create_folder')" :title="$t('actions.create_folder')"
icon="folder-plus" icon="folder-plus"
:is-hover-disabled="true" :is-hover-disabled="true"
/> />
</OptionGroup> </OptionGroup>
</MobileCreateMenu> </MobileCreateMenu>
<MobileMultiSelectToolbar> <MobileMultiSelectToolbar>
<ToolbarButton <ToolbarButton
@click.native="$moveFileOrFolder(clipboard)" @click.native="$moveFileOrFolder(clipboard)"
class="action-btn" class="action-btn"
source="move" source="move"
:action="$t('actions.move')" :action="$t('actions.move')"
:class="{ 'is-inactive': clipboard.length < 1 }" :class="{ 'is-inactive': clipboard.length < 1 }"
/> />
<ToolbarButton <ToolbarButton
@click.native="$deleteFileOrFolder(clipboard)" @click.native="$deleteFileOrFolder(clipboard)"
class="action-btn" class="action-btn"
source="trash" source="trash"
:class="{ 'is-inactive': clipboard.length < 1 }" :class="{ 'is-inactive': clipboard.length < 1 }"
:action="$t('actions.delete')" :action="$t('actions.delete')"
/> />
</MobileMultiSelectToolbar> </MobileMultiSelectToolbar>
<ContextMenu v-if="entries.length"> <ContextMenu v-if="entries.length">
@@ -48,20 +48,20 @@
</OptionGroup> </OptionGroup>
<OptionGroup> <OptionGroup>
<Option <Option
@click.native="$createFolder" @click.native="$createFolder"
:title="$t('context_menu.create_folder')" :title="$t('context_menu.create_folder')"
icon="create-folder" icon="create-folder"
/> />
</OptionGroup> </OptionGroup>
</template> </template>
<template v-slot:single-select v-if="item"> <template v-slot:single-select v-if="item">
<OptionGroup> <OptionGroup>
<Option <Option
@click.native="$renameFileOrFolder(item)" @click.native="$renameFileOrFolder(item)"
:title="$t('context_menu.rename')" :title="$t('context_menu.rename')"
icon="rename" icon="rename"
/> />
<Option @click.native="$moveFileOrFolder(item)" :title="$t('context_menu.move')" icon="move-item" /> <Option @click.native="$moveFileOrFolder(item)" :title="$t('context_menu.move')" icon="move-item" />
<Option @click.native="$deleteFileOrFolder(item)" :title="$t('context_menu.delete')" icon="trash" /> <Option @click.native="$deleteFileOrFolder(item)" :title="$t('context_menu.delete')" icon="trash" />
</OptionGroup> </OptionGroup>
@@ -86,10 +86,10 @@
{{ $t('Spotlight') }} {{ $t('Spotlight') }}
</MobileActionButton> </MobileActionButton>
<MobileActionButton <MobileActionButton
@click.native="$showMobileMenu('create-list')" @click.native="$showMobileMenu('create-list')"
v-if="$checkPermission(['master', 'editor'])" v-if="$checkPermission(['master', 'editor'])"
icon="cloud-plus" icon="cloud-plus"
> >
{{ $t('mobile.create') }} {{ $t('mobile.create') }}
</MobileActionButton> </MobileActionButton>
<MobileActionButton @click.native="$enableMultiSelectMode" icon="check-square"> <MobileActionButton @click.native="$enableMultiSelectMode" icon="check-square">
@@ -100,28 +100,26 @@
</MobileActionButton> </MobileActionButton>
</FileActionsMobile> </FileActionsMobile>
<EmptyFilePage> <EmptyFilePage v-if="uploadRequest">
<div v-if="uploadRequest" class="relative mx-auto mb-8 w-24 text-center"> <div class="relative mx-auto mb-8 w-24 text-center">
<VueFolderIcon class="inline-block w-28" /> <VueFolderIcon class="inline-block w-28" />
<MemberAvatar <MemberAvatar
:member="uploadRequest.data.relationships.user" v-if="uploadRequest.data.attributes.status !== 'expired'"
class="absolute -bottom-2.5 -right-2" :member="uploadRequest.data.relationships.user"
:is-border="true" class="absolute -bottom-2.5 -right-2"
:size="32" :is-border="true"
/> :size="32"
/>
</div> </div>
<h1 class="title"> <h1 class="title">
{{ $t('Jane Request You for File Upload') }} {{ emptyPageTitle }}
</h1> </h1>
<p class="description max-w-[420px]"> <p class="description max-w-[420px]">
{{ {{ emptyPageDescription }}
$t(
'Your files will be uploaded automatically and after that, you can organize your files in folders.'
)
}}
</p> </p>
<ButtonUpload button-style="theme">
<ButtonUpload v-if="uploadRequest.data.attributes.status === 'active'" button-style="theme">
{{ $t('empty_page.call_to_action') }} {{ $t('empty_page.call_to_action') }}
</ButtonUpload> </ButtonUpload>
</EmptyFilePage> </EmptyFilePage>
@@ -146,51 +144,65 @@ import FileBrowser from '../../components/FilesView/FileBrowser'
import ContextMenu from '../../components/FilesView/ContextMenu' import ContextMenu from '../../components/FilesView/ContextMenu'
import OptionGroup from '../../components/FilesView/OptionGroup' import OptionGroup from '../../components/FilesView/OptionGroup'
import Option from '../../components/FilesView/Option' import Option from '../../components/FilesView/Option'
import { events } from '../../bus' import {events} from '../../bus'
import { mapGetters } from 'vuex' import {mapGetters} from 'vuex'
export default { export default {
name: 'Files', name: 'Files',
components: { components: {
MobileMultiSelectToolbar, MobileMultiSelectToolbar,
MobileActionButton, MobileActionButton,
FileActionsMobile, FileActionsMobile,
MobileContextMenu, MobileContextMenu,
MobileCreateMenu, MobileCreateMenu,
EmptyFilePage, EmptyFilePage,
VueFolderIcon, VueFolderIcon,
ToolbarButton, ToolbarButton,
MemberAvatar, MemberAvatar,
ButtonUpload, ButtonUpload,
OptionUpload, OptionUpload,
OptionGroup, OptionGroup,
FileBrowser, FileBrowser,
ContextMenu, ContextMenu,
Option, Option,
}, },
computed: { computed: {
...mapGetters(['fastPreview', 'clipboard', 'config', 'user', 'entries', 'uploadRequest']), ...mapGetters(['fastPreview', 'clipboard', 'config', 'user', 'entries', 'uploadRequest']),
isFolder() { isFolder() {
return this.item && this.item.data.type === 'folder' return this.item && this.item.data.type === 'folder'
}, },
}, emptyPageTitle() {
data() { // Todo: add name into translation
return { return {
item: undefined, active: this.$t('Jane Request You for File Upload'),
} filled: this.$t('Upload Request was Fulfilled Successfully'),
}, expired: this.$t('Upload Request Expired'),
methods: { }[this.uploadRequest.data.attributes.status]
createFolder() { },
events.$emit('popup:open', { name: 'create-folder' }) emptyPageDescription() {
}, return {
}, active: this.$t('Your files will be uploaded automatically and after that, you can organize your files in folders.'),
created() { filled: this.$t('This upload request is no longer available for uploading files.'),
expired: this.$t('This upload request is no longer available for uploading files.'),
}[this.uploadRequest.data.attributes.status]
}
},
data() {
return {
item: undefined,
}
},
methods: {
createFolder() {
events.$emit('popup:open', {name: 'create-folder'})
},
},
created() {
events.$on('context-menu:show', (event, item) => (this.item = item)) events.$on('context-menu:show', (event, item) => (this.item = item))
events.$on('context-menu:current-folder', (folder) => (this.item = folder)) events.$on('context-menu:current-folder', (folder) => (this.item = folder))
events.$on('mobile-context-menu:show', (item) => (this.item = item)) events.$on('mobile-context-menu:show', (item) => (this.item = item))
this.$store.dispatch('getUploadRequestDetail') this.$store.dispatch('getUploadRequestDetail')
},
},
} }
</script> </script>
+9
View File
@@ -4,6 +4,8 @@
<FilePreview /> <FilePreview />
<Spotlight /> <Spotlight />
<ConfirmPopup />
<!--Popups--> <!--Popups-->
<CreateFolderPopup /> <CreateFolderPopup />
<RenameItemPopup /> <RenameItemPopup />
@@ -42,6 +44,7 @@ import FileSortingMobile from '../components/FilesView/FileSortingMobile'
import FileFilterMobile from '../components/FilesView/FileFilterMobile' import FileFilterMobile from '../components/FilesView/FileFilterMobile'
import CreateFolderPopup from '../components/Others/CreateFolderPopup' import CreateFolderPopup from '../components/Others/CreateFolderPopup'
import DesktopToolbar from '../components/FilesView/DesktopToolbar' import DesktopToolbar from '../components/FilesView/DesktopToolbar'
import ConfirmPopup from "../components/Others/Popup/ConfirmPopup"
import RenameItemPopup from '../components/Others/RenameItemPopup' import RenameItemPopup from '../components/Others/RenameItemPopup'
import MobileToolbar from '../components/FilesView/MobileToolbar' import MobileToolbar from '../components/FilesView/MobileToolbar'
import FilePreview from '../components/FilePreview/FilePreview' import FilePreview from '../components/FilePreview/FilePreview'
@@ -55,6 +58,7 @@ import { events } from '../bus'
export default { export default {
name: 'UploadRequest', name: 'UploadRequest',
components: { components: {
ConfirmPopup,
DesktopUploadRequestToolbar, DesktopUploadRequestToolbar,
CreateFolderPopup, CreateFolderPopup,
FileSortingMobile, FileSortingMobile,
@@ -85,6 +89,11 @@ export default {
// TODO: new scaledown effect // TODO: new scaledown effect
events.$on('mobile-menu:show', () => (this.isScaledDown = true)) events.$on('mobile-menu:show', () => (this.isScaledDown = true))
events.$on('mobile-menu:hide', () => (this.isScaledDown = false)) events.$on('mobile-menu:hide', () => (this.isScaledDown = false))
events.$on('action:confirmed', (data) => {
if (data.operation === 'close-upload-request')
this.$store.dispatch('closeUploadRequest')
})
}, },
} }
</script> </script>
+3 -1
View File
@@ -2,10 +2,12 @@
use Domain\UploadRequest\Controllers\GetUploadRequestController; use Domain\UploadRequest\Controllers\GetUploadRequestController;
use Domain\UploadRequest\Controllers\CreateUploadRequestController; use Domain\UploadRequest\Controllers\CreateUploadRequestController;
use Domain\UploadRequest\Controllers\SetUploadRequestAsFilledController;
use Domain\UploadRequest\Controllers\UploadFilesForUploadRequestController; use Domain\UploadRequest\Controllers\UploadFilesForUploadRequestController;
Route::get('/{uploadRequest}', GetUploadRequestController::class); Route::get('/{uploadRequest}', GetUploadRequestController::class);
Route::post('/{uploadRequest}', UploadFilesForUploadRequestController::class); Route::delete('/{uploadRequest}', SetUploadRequestAsFilledController::class);
Route::post('/{uploadRequest}/upload', UploadFilesForUploadRequestController::class);
Route::group(['middleware' => ['auth:sanctum']], function () { Route::group(['middleware' => ['auth:sanctum']], function () {
Route::post('/', CreateUploadRequestController::class); Route::post('/', CreateUploadRequestController::class);
@@ -0,0 +1,21 @@
<?php
namespace Domain\UploadRequest\Controllers;
use Domain\UploadRequest\Models\UploadRequest;
use Domain\UploadRequest\Resources\UploadRequestResource;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Response;
class SetUploadRequestAsFilledController
{
public function __invoke(UploadRequest $uploadRequest): Response|Application|ResponseFactory
{
$uploadRequest->update([
'status' => 'filled',
]);
return response(new UploadRequestResource($uploadRequest), 201);
}
}
@@ -28,7 +28,7 @@ class UploadRequestResource extends JsonResource
'id' => $this->user->id, 'id' => $this->user->id,
'type' => 'user', 'type' => 'user',
'attributes' => [ 'attributes' => [
'name' => $this->user->settings->name, 'name' => $this->user->settings->first_name,
'avatar' => $this->user->settings->avatar, 'avatar' => $this->user->settings->avatar,
], ],
], ],
@@ -1,4 +1,5 @@
<?php <?php
namespace Tests\Domain\UploadRequest; namespace Tests\Domain\UploadRequest;
use Storage; use Storage;
@@ -118,7 +119,7 @@ class UploadRequestTest extends TestCase
->create('fake-file.pdf', 12000000, 'application/pdf'); ->create('fake-file.pdf', 12000000, 'application/pdf');
$this $this
->postJson("/api/upload-request/$uploadRequest->id", [ ->postJson("/api/upload-request/$uploadRequest->id/upload", [
'filename' => $file->name, 'filename' => $file->name,
'file' => $file, 'file' => $file,
'parent_id' => null, 'parent_id' => null,
@@ -159,7 +160,7 @@ class UploadRequestTest extends TestCase
->create('fake-file.pdf', 12000000, 'application/pdf'); ->create('fake-file.pdf', 12000000, 'application/pdf');
$this $this
->postJson("/api/upload-request/$uploadRequest->id", [ ->postJson("/api/upload-request/$uploadRequest->id/upload", [
'filename' => $file->name, 'filename' => $file->name,
'file' => $file, 'file' => $file,
'parent_id' => null, 'parent_id' => null,
@@ -167,4 +168,28 @@ class UploadRequestTest extends TestCase
'is_last' => 'true', 'is_last' => 'true',
])->assertStatus(410); ])->assertStatus(410);
} }
/**
* @test
*/
public function it_mark_upload_request_as_filled()
{
$user = User::factory()
->hasSettings()
->create();
$uploadRequest = UploadRequest::factory()
->create([
'status' => 'active',
'user_id' => $user->id,
]);
$this
->deleteJson("/api/upload-request/$uploadRequest->id")
->assertStatus(201)
->assertJsonFragment([
'id' => $uploadRequest->id,
'status' => 'filled',
]);
}
} }