Radu Liviu Carjan
3 years ago
29 changed files with 45631 additions and 33714 deletions
-
45app/Http/Controllers/FileController.php
-
68app/Http/Controllers/FilterController.php
-
24app/Http/Controllers/PagesController.php
-
42906package-lock.json
-
15package.json
-
572public/css/app.css
-
30790public/js/app.js
-
2resources/js/SearchDisplace/Cookie.ts
-
36resources/js/app.js
-
64resources/js/app.ts
-
12resources/js/bootstrap.ts
-
125resources/js/components/Home.vue
-
157resources/js/components/ProcessFile.vue
-
69resources/js/components/helpers/Filter.vue
-
13resources/js/components/layout/Footer.vue
-
15resources/js/components/layout/Header.vue
-
6resources/js/interfaces/FileData.ts
-
7resources/js/interfaces/FilterInterface.ts
-
4resources/js/interfaces/FilterOptions.ts
-
13resources/js/shims-tsx.d.ts
-
4resources/js/shims-vue.d.ts
-
4resources/views/app.blade.php
-
6resources/views/pages/home.blade.php
-
52routes/api.php
-
10routes/web.php
-
3run-dev.sh
-
38tsconfig.json
-
13webpack.mix.js
-
4272yarn.lock
@ -0,0 +1,45 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use Illuminate\Contracts\Container\BindingResolutionException; |
|||
use Illuminate\Http\JsonResponse; |
|||
use Illuminate\Support\Facades\Storage; |
|||
|
|||
class FileController extends Controller |
|||
{ |
|||
|
|||
/** |
|||
* |
|||
* @return JsonResponse |
|||
* |
|||
* @throws BindingResolutionException |
|||
*/ |
|||
public function create(): JsonResponse |
|||
{ |
|||
$file = request()->file('file'); |
|||
$filename = time() . $file->getClientOriginalName(); |
|||
|
|||
$path = Storage::putFileAs('files/', $file, $filename); |
|||
|
|||
return response()->json([ |
|||
'file' => $file->getClientOriginalName(), |
|||
'id' => $filename, |
|||
'path' => $path |
|||
]); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* |
|||
* @param string $id |
|||
* |
|||
* @return JsonResponse |
|||
* |
|||
* @throws BindingResolutionException |
|||
*/ |
|||
public function get(string $id): JsonResponse |
|||
{ |
|||
return response()->json(['success']); |
|||
} |
|||
} |
@ -0,0 +1,68 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use Illuminate\Contracts\Container\BindingResolutionException; |
|||
use Illuminate\Http\JsonResponse; |
|||
|
|||
class FilterController extends Controller |
|||
{ |
|||
public static array $filters = [ |
|||
'phone_number' => [ |
|||
'display_name' => 'Phone number', |
|||
'options' => [ |
|||
'replace_with' => [ |
|||
'#', '*', 'x', |
|||
] |
|||
] |
|||
], |
|||
'email' => [ |
|||
'display_name' => 'Email', |
|||
'options' => [ |
|||
'replace_with' => [ |
|||
'#', '*', 'x', |
|||
], |
|||
'include_domain' => [ |
|||
true, false |
|||
] |
|||
] |
|||
] |
|||
]; |
|||
|
|||
/** |
|||
* Create a new filter |
|||
* |
|||
* @return JsonResponse |
|||
* |
|||
* @throws BindingResolutionException |
|||
*/ |
|||
public function create(): JsonResponse |
|||
{ |
|||
return response()->json([ |
|||
'result' => 'success' |
|||
]); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Get one or all the filters we have |
|||
* |
|||
* @param string|null $id The ID of the filter. Null if we want all of them |
|||
* |
|||
* @return JsonResponse |
|||
* |
|||
* @throws BindingResolutionException |
|||
*/ |
|||
public function get(string $id = null): JsonResponse |
|||
{ |
|||
if ($id === null) { |
|||
return response()->json(static::$filters); |
|||
} |
|||
|
|||
if (!empty(static::$filters[$id])) { |
|||
return response()-> json(static::$filters[$id]); |
|||
} |
|||
|
|||
abort(404); |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use Illuminate\View\View; |
|||
use Illuminate\Contracts\View\Factory; |
|||
use Illuminate\Contracts\Container\BindingResolutionException; |
|||
|
|||
class PagesController extends Controller |
|||
{ |
|||
|
|||
/** |
|||
* Return the home page view |
|||
* |
|||
* @return View|Factory |
|||
* |
|||
* @throws BindingResolutionException |
|||
*/ |
|||
public function home(): View |
|||
{ |
|||
$filters = FilterController::$filters; |
|||
return view('pages.home')->with('filters', $filters); |
|||
} |
|||
} |
42906
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
572
public/css/app.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
30790
public/js/app.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,36 +0,0 @@ |
|||
require('./bootstrap'); |
|||
|
|||
window.Vue = require('vue'); |
|||
|
|||
/** |
|||
* Import vendor classes |
|||
*/ |
|||
import PrimeVue from 'primevue/config'; |
|||
import Button from 'primevue/button'; |
|||
import Card from 'primevue/card'; |
|||
import Sidebar from 'primevue/sidebar'; |
|||
import OrderList from 'primevue/orderlist'; |
|||
|
|||
// Own components
|
|||
import Header from './layout/Header'; |
|||
import Footer from './layout/Footer'; |
|||
import Home from './components/Home'; |
|||
|
|||
window.Event = new Vue(); |
|||
|
|||
Vue.use(PrimeVue, { |
|||
ripple: true, |
|||
}); |
|||
|
|||
Vue.component('Button', Button) |
|||
.component('Card', Card) |
|||
.component('Sidebar', Sidebar) |
|||
.component('OrderList', OrderList); |
|||
|
|||
Vue.component('home', Home) |
|||
.component('Header', Header) |
|||
.component('Footer', Footer); |
|||
|
|||
new Vue({ |
|||
el: '#app', |
|||
}); |
@ -0,0 +1,64 @@ |
|||
import './bootstrap'; |
|||
import Vue from 'vue'; |
|||
|
|||
/** |
|||
* Import vendor classes |
|||
*/ |
|||
import PrimeVue from 'primevue/config'; |
|||
import Button from 'primevue/button'; |
|||
import Panel from 'primevue/panel'; |
|||
import Card from 'primevue/card'; |
|||
import FileUpload from 'primevue/fileupload'; |
|||
import BlockUI from 'primevue/blockui'; |
|||
import Sidebar from 'primevue/sidebar'; |
|||
import OrderList from 'primevue/orderlist'; |
|||
import Chip from 'primevue/chip'; |
|||
import Divider from 'primevue/divider'; |
|||
import Listbox from 'primevue/listbox'; |
|||
import Toolbar from 'primevue/toolbar'; |
|||
import Skeleton from 'primevue/skeleton'; |
|||
import ToastService from 'primevue/toastservice'; |
|||
import Toast from 'primevue/toast'; |
|||
import Fieldset from 'primevue/fieldset'; |
|||
|
|||
|
|||
// Own components
|
|||
import AppHeader from './components/layout/Header.vue'; |
|||
import AppFooter from './components/layout/Footer.vue'; |
|||
import Home from './components/Home.vue'; |
|||
import ProcessFile from './components/ProcessFile.vue'; |
|||
import Filter from './components/helpers/Filter.vue'; |
|||
import SelectButton from 'primevue/selectbutton'; |
|||
|
|||
|
|||
Vue.use(PrimeVue, { |
|||
ripple: true, |
|||
}); |
|||
|
|||
Vue.use(ToastService); |
|||
|
|||
Vue.component('Button', Button); |
|||
Vue.component('Panel', Panel); |
|||
Vue.component('Card', Card); |
|||
Vue.component('FileUpload', FileUpload); |
|||
Vue.component('BlockUI', BlockUI); |
|||
Vue.component('Sidebar', Sidebar); |
|||
Vue.component('OrderList', OrderList); |
|||
Vue.component('Chip', Chip); |
|||
Vue.component('Divider', Divider); |
|||
Vue.component('Listbox', Listbox); |
|||
Vue.component('Toolbar', Toolbar); |
|||
Vue.component('Skeleton', Skeleton); |
|||
Vue.component('Toast', Toast); |
|||
Vue.component('SelectButton', SelectButton); |
|||
Vue.component('Fieldset', Fieldset); |
|||
|
|||
Vue.component('home', Home); |
|||
Vue.component('process-file', ProcessFile); |
|||
Vue.component('filter-view', Filter); |
|||
Vue.component('app-header', AppHeader); |
|||
Vue.component('app-footer', AppFooter); |
|||
|
|||
new Vue({ |
|||
el: '#app', |
|||
}); |
@ -1,107 +1,84 @@ |
|||
<template> |
|||
<div class="wrap"> |
|||
<Sidebar v-model:visible="filesSidebarVisible" class="p-sidebar-lg"> |
|||
<Listbox v-model="selectedFile" :options="files" optionLabel="file"> |
|||
<template #option="slotProps"> |
|||
<div> |
|||
<span>{{ slotProps.option.file.name }}</span> |
|||
<Chip :label="slotProps.option.status" /> |
|||
</div> |
|||
</template> |
|||
</Listbox> |
|||
</Sidebar> |
|||
|
|||
<div class="p-d-flex p-flex-row p-jc-between p-ai-stretch"> |
|||
<Panel |
|||
v-if="selectedFile !== null" |
|||
class="p-mr-2 p-as-stretch file-card" |
|||
> |
|||
<template #header> |
|||
{{ selectedFile.file.name }} |
|||
</template> |
|||
<div class="p-d-flex p-flex-row p-jc-between p-ai-stretch"> |
|||
<Panel |
|||
class="p-mr-2 p-as-stretch file-card" |
|||
> |
|||
<template #header> |
|||
File preview |
|||
</template> |
|||
|
|||
<OrderList |
|||
v-model="selectedFilters" |
|||
listStyle="height:auto" |
|||
dataKey="file" |
|||
> |
|||
<template #header> Selected filters </template> |
|||
<template #item="slotProps"> |
|||
<div class="p-item" @click="selectItem(item)"> |
|||
<div> |
|||
<span class="p-caritem-vin">{{ |
|||
slotProps.item.file.name |
|||
}}</span> |
|||
<Chip :label="slotProps.item.status" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
</OrderList> |
|||
</Panel> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
<Skeleton /><br /> |
|||
</Panel> |
|||
|
|||
<Card class="p-mr-2 p-as-stretch filters-card"> |
|||
<template #header> |
|||
<Toolbar> |
|||
<template #left> |
|||
<h3>Available filters</h3> |
|||
</template> |
|||
<template #right> |
|||
<Button |
|||
icon="pi pi-plus" |
|||
class="p-button-success" |
|||
/> |
|||
</template> |
|||
</Toolbar> |
|||
</template> |
|||
<Card class="p-mr-2 p-as-stretch filters-card"> |
|||
<template #header> |
|||
<Toolbar> |
|||
<template #left> |
|||
<h3>Available filters</h3> |
|||
</template> |
|||
<template #right> |
|||
<Button |
|||
icon="pi pi-plus" |
|||
class="p-button-success" |
|||
/> |
|||
</template> |
|||
</Toolbar> |
|||
</template> |
|||
|
|||
<template #content> CONTENT </template> |
|||
</Card> |
|||
</div> |
|||
<template #content> |
|||
<filter-view |
|||
v-for="(filter, id, index) in filters" |
|||
:key="index" |
|||
:id="id" |
|||
:display-name="filter.display_name" |
|||
:options="filter.options" |
|||
></filter-view> |
|||
</template> |
|||
</Card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
uiBlocked: false, |
|||
filesSidebarVisible: false, |
|||
selectedFile: null, |
|||
selectedFilters: [], |
|||
}; |
|||
}, |
|||
<script lang="ts"> |
|||
import axios from 'axios'; |
|||
import { Vue, Component, Prop } from 'vue-property-decorator'; |
|||
import { FileData } from '../interfaces/FileData'; |
|||
import { FilterInterface } from '../interfaces/FilterInterface'; |
|||
|
|||
props: { |
|||
files: { |
|||
required: true, |
|||
type: Array, |
|||
}, |
|||
}, |
|||
@Component |
|||
export default class ProcessFile extends Vue { |
|||
|
|||
methods: { |
|||
selectItem(item) { |
|||
console.log('SELECTING ITEM: ', item); |
|||
this.selectedFile = item; |
|||
this.filesSidebarVisible = false; |
|||
} |
|||
}, |
|||
@Prop({ default: null }) |
|||
public readonly file!: FileData|null; |
|||
|
|||
created() { |
|||
this.files = []; |
|||
@Prop({ default: [] }) |
|||
public readonly filters!: { [keys:string]: FilterInterface } |
|||
|
|||
setTimeout( |
|||
() => { |
|||
this.filesSidebarVisible = true; |
|||
}, |
|||
500, |
|||
); |
|||
}, |
|||
}; |
|||
private selectedFile: File|null = null; |
|||
private selectedFilters = []; |
|||
|
|||
/** |
|||
* |
|||
*/ |
|||
created() { |
|||
console.log('FILE: ', this.file); |
|||
console.log('FILTERS: ', this.filters); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.file-card, |
|||
<style lang="scss"> |
|||
.file-card { |
|||
flex: 0 1 66%; |
|||
} |
|||
|
|||
.filters-card { |
|||
flex: 0 1 48%; |
|||
flex: 0 1 32% |
|||
} |
|||
</style> |
@ -0,0 +1,69 @@ |
|||
<template> |
|||
<Fieldset :legend="displayName" :toggleable="true" class="filter-container"> |
|||
|
|||
<div v-for="option of optionsList" :key="option.name" class="filter-option"> |
|||
<h5>{{ option.name }}</h5> |
|||
|
|||
<SelectButton |
|||
:key="option.name" |
|||
v-model="selectedOption" |
|||
:options="option.list" |
|||
optionLabel="name" /> |
|||
</div> |
|||
</Fieldset> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
|
|||
import { Vue, Component, Prop } from 'vue-property-decorator'; |
|||
import { FilterOptions } from '../../interfaces/FilterOptions'; |
|||
|
|||
@Component |
|||
export default class Filter extends Vue { |
|||
|
|||
@Prop(String) |
|||
public readonly id!: string; |
|||
|
|||
@Prop(String) |
|||
public readonly displayName!: string; |
|||
|
|||
@Prop({ default: [] }) |
|||
public readonly options!: FilterOptions; |
|||
|
|||
private optionsList = new Array; |
|||
|
|||
public selectedOption = null; |
|||
|
|||
public created() |
|||
{ |
|||
for (let index in this.options) { |
|||
let words = index.split('_'); |
|||
for (let i = 0; i < words.length; i++) { |
|||
words[i] = words[i].charAt(0).toUpperCase() + words[i].substr(1); |
|||
} |
|||
|
|||
|
|||
|
|||
let option = { |
|||
name: words.join(' '), |
|||
list: new Array |
|||
}; |
|||
this.options[index].forEach( opt => { |
|||
option.list.push({ |
|||
'name': opt, |
|||
'value': opt |
|||
}); |
|||
}); |
|||
|
|||
this.optionsList.push(option); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.filter-container, |
|||
.filter-option { |
|||
margin-bottom: 10px; |
|||
} |
|||
</style> |
@ -0,0 +1,6 @@ |
|||
export interface FileData |
|||
{ |
|||
file: string; |
|||
id: string; |
|||
path: string; |
|||
} |
@ -0,0 +1,7 @@ |
|||
import { FilterOptions } from "./FilterOptions"; |
|||
|
|||
export interface FilterInterface |
|||
{ |
|||
display_name: string; |
|||
options: FilterOptions; |
|||
} |
@ -0,0 +1,4 @@ |
|||
export interface FilterOptions |
|||
{ |
|||
[keys: string]: string[] |
|||
} |
@ -0,0 +1,13 @@ |
|||
import Vue, { VNode } from 'vue'; |
|||
|
|||
declare global { |
|||
namespace JSX { |
|||
// tslint:disable no-empty-interface
|
|||
interface Element extends VNode {} |
|||
// tslint:disable no-empty-interface
|
|||
interface ElementClass extends Vue {} |
|||
interface IntrinsicElements { |
|||
[elem: string]: any; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
declare module '*.vue' { |
|||
import Vue from 'vue' |
|||
export default Vue |
|||
} |
@ -0,0 +1,3 @@ |
|||
#!/bin/zsh |
|||
|
|||
clear && npm run watch |
@ -0,0 +1,38 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "esnext", |
|||
"module": "esnext", |
|||
"strict": true, |
|||
"jsx": "preserve", |
|||
"importHelpers": true, |
|||
"moduleResolution": "node", |
|||
"experimentalDecorators": true, |
|||
"esModuleInterop": true, |
|||
"allowSyntheticDefaultImports": true, |
|||
"sourceMap": true, |
|||
"baseUrl": ".", |
|||
"types": [ |
|||
"webpack-env" |
|||
], |
|||
"lib": [ |
|||
"esnext", |
|||
"dom", |
|||
"dom.iterable", |
|||
"scripthost" |
|||
], |
|||
"paths": { |
|||
"@/*": [ |
|||
"resources/js/*" |
|||
], |
|||
"@components/*": [ |
|||
"resources/js/components/*" |
|||
], |
|||
}, |
|||
}, |
|||
"include": [ |
|||
"resources/js/**/*" |
|||
], |
|||
"exclude": [ |
|||
"node_modules" |
|||
] |
|||
} |
4272
yarn.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue