Browse Source

added feature: jobs can now have file attachments

pull/43/head
Damilola Olowookere 7 years ago
parent
commit
b28a104662
  1. 205
      app/Entities/Projects/Job.php
  2. 13
      app/Entities/Projects/JobsRepository.php
  3. 298
      app/Http/Controllers/JobsController.php
  4. 241
      app/Http/Controllers/Projects/FilesController.php
  5. 150
      config/medialibrary.php
  6. 38
      database/migrations/2019_06_29_145856_create_media_table.php
  7. 68
      resources/lang/de/auth.php
  8. 93
      resources/lang/de/job.php
  9. 68
      resources/lang/en/auth.php
  10. 95
      resources/lang/en/job.php
  11. 68
      resources/lang/id/auth.php
  12. 95
      resources/lang/id/job.php
  13. 12
      resources/views/jobs/edit.blade.php
  14. 11
      resources/views/jobs/partials/job-show.blade.php
  15. 193
      routes/web/projects.php

205
app/Entities/Projects/Job.php

@ -2,127 +2,130 @@
namespace App\Entities\Projects;
use DB;
use App\Entities\Users\User;
use DB;
use Illuminate\Database\Eloquent\Model;
use Laracasts\Presenter\PresentableTrait;
use Spatie\MediaLibrary\HasMedia\HasMedia;
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
/**
* Job Model.
*
* @author Nafies Luthfi <nafiesl@gmail.com>
*/
class Job extends Model
class Job extends Model implements HasMedia
{
use PresentableTrait;
use PresentableTrait;
use HasMediaTrait;
/**
* @var \App\Entities\Projects\JobPresenter
*/
protected $presenter = JobPresenter::class;
/**
* @var \App\Entities\Projects\JobPresenter
*/
protected $presenter = JobPresenter::class;
/**
* @var array
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
/**
* @var array
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
/**
* Show job name with link to job detail.
*
* @return Illuminate\Support\HtmlString
*/
public function nameLink()
{
return link_to_route('jobs.show', $this->name, [$this->id], [
'title' => __(
'app.show_detail_title',
['name' => $this->name, 'type' => __('job.job')]
),
]);
}
/**
* Show job name with link to job detail.
*
* @return Illuminate\Support\HtmlString
*/
public function nameLink()
{
return link_to_route('jobs.show', $this->name, [$this->id], [
'title' => __(
'app.show_detail_title',
['name' => $this->name, 'type' => __('job.job')]
),
]);
}
/**
* Job belongs to a Project relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function project()
{
return $this->belongsTo(Project::class, 'project_id');
}
/**
* Job belongs to a Project relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function project()
{
return $this->belongsTo(Project::class, 'project_id');
}
/**
* Job belongs to a Worker relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function worker()
{
return $this->belongsTo(User::class, 'worker_id');
}
/**
* Job belongs to a Worker relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function worker()
{
return $this->belongsTo(User::class, 'worker_id');
}
/**
* Job has many Tasks relation ordered by position.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tasks()
{
return $this->hasMany(Task::class)->orderBy('position');
}
/**
* Job has many Tasks relation ordered by position.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tasks()
{
return $this->hasMany(Task::class)->orderBy('position');
}
/**
* Job has many comments relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
/**
* Job has many comments relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
/**
* Get the job type.
*
* @return string
*/
public function type()
{
return $this->type_id == 1 ? __('job.main') : __('job.additional');
}
/**
* Get the job type.
*
* @return string
*/
public function type()
{
return $this->type_id == 1 ? __('job.main') : __('job.additional');
}
/**
* Add progress attribute on Job model.
*
* @return int
*/
public function getProgressAttribute()
{
return $this->tasks->isEmpty() ? 0 : $this->tasks->avg('progress');
}
/**
* Add progress attribute on Job model.
*
* @return int
*/
public function getProgressAttribute()
{
return $this->tasks->isEmpty() ? 0 : $this->tasks->avg('progress');
}
/**
* Add receiveable_earning attribute on Job model.
*
* @return float
*/
public function getReceiveableEarningAttribute()
{
return $this->price * ($this->progress / 100);
}
/**
* Add receiveable_earning attribute on Job model.
*
* @return float
*/
public function getReceiveableEarningAttribute()
{
return $this->price * ($this->progress / 100);
}
/**
* Delete the model from the database.
*
* @return bool|null
*/
public function delete()
{
DB::beginTransaction();
$this->tasks()->delete();
$this->comments()->delete();
DB::commit();
/**
* Delete the model from the database.
*
* @return bool|null
*/
public function delete()
{
DB::beginTransaction();
$this->tasks()->delete();
$this->comments()->delete();
DB::commit();
return parent::delete();
}
return parent::delete();
}
}

13
app/Entities/Projects/JobsRepository.php

@ -82,8 +82,19 @@ class JobsRepository extends BaseRepository
}
$jobData['price'] = str_replace('.', '', $jobData['price']);
$jobData = collect( $jobData);
$job = $this->requireById($jobId);
$job->update($jobData);
if($jobData->has('files')){
foreach ($jobData->get('files') as $file) {
$path = $file->getPathName();
if(is_file($path)){
$job->addMedia($path)->toMediaCollection();
}
}
}
$job->update($jobData->except('files')->toArray());
return $job;
}

298
app/Http/Controllers/JobsController.php

@ -2,13 +2,13 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Entities\Projects\Job;
use App\Entities\Projects\Comment;
use App\Entities\Projects\Project;
use App\Entities\Projects\Job;
use App\Entities\Projects\JobsRepository;
use App\Entities\Projects\Project;
use App\Http\Requests\Jobs\DeleteRequest;
use App\Http\Requests\Jobs\UpdateRequest;
use Illuminate\Http\Request;
/**
* Jobs Controller.
@ -17,149 +17,151 @@ use App\Http\Requests\Jobs\UpdateRequest;
*/
class JobsController extends Controller
{
/**
* @var \App\Entities\Projects\JobsRepository
*/
private $repo;
/**
* Create new Jobs Controller.
*
* @param \App\Entities\Projects\JobsRepository $repo
*/
public function __construct(JobsRepository $repo)
{
$this->repo = $repo;
}
/**
* Show unfinished job list.
*
* @return \Illuminate\View\View
*/
public function index()
{
$user = auth()->user();
if ($user->hasRole('admin')) {
$projects = Project::whereIn('status_id', [2, 3])->pluck('name', 'id');
} else {
$projects = $user->projects()
->whereIn('status_id', [2, 3])
->pluck('projects.name', 'projects.id');
}
$jobs = $this->repo->getUnfinishedJobs($user, request('project_id'));
return view('jobs.unfinished', compact('jobs', 'projects'));
}
/**
* Show a job detail.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function show(Request $request, Job $job)
{
$this->authorize('view', $job);
$editableTask = null;
$editableComment = null;
$comments = $job->comments()->with('creator')->latest()->paginate();
if ($request->get('action') == 'task_edit' && $request->has('task_id')) {
$editableTask = $this->repo->requireTaskById($request->get('task_id'));
}
if ($request->get('action') == 'task_delete' && $request->has('task_id')) {
$editableTask = $this->repo->requireTaskById($request->get('task_id'));
}
if (request('action') == 'comment-edit' && request('comment_id') != null) {
$editableComment = Comment::find(request('comment_id'));
}
return view('jobs.show', compact('job', 'editableTask', 'comments', 'editableComment'));
}
/**
* Show a job edit form.
*
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function edit(Job $job)
{
$this->authorize('view', $job);
$workers = $this->repo->getWorkersList();
return view('jobs.edit', compact('job', 'workers'));
}
/**
* Update a job on database.
*
* @param \App\Http\Requests\Jobs\UpdateRequest $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\Routing\Redirector
*/
public function update(UpdateRequest $request, Job $job)
{
$job = $this->repo->update($request->except(['_method', '_token']), $job->id);
flash(trans('job.updated'), 'success');
return redirect()->route('jobs.show', $job);
}
/**
* Show job delete confirmation page.
*
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function delete(Job $job)
{
return view('jobs.delete', compact('job'));
}
/**
* Show job delete confirmation page.
*
* @param \App\Http\Requests\Jobs\DeleteRequest $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function destroy(DeleteRequest $request, Job $job)
{
$projectId = $job->project_id;
if ($job->id == $request->get('job_id')) {
$job->tasks()->delete();
$job->delete();
flash(trans('job.deleted'), 'success');
} else {
flash(trans('job.undeleted'), 'danger');
}
return redirect()->route('projects.jobs.index', $projectId);
}
/**
* Reorder job task position.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Job $job
* @return string|null
*/
public function tasksReorder(Request $request, Job $job)
{
if ($request->expectsJson()) {
$data = $this->repo->tasksReorder($request->get('postData'));
return 'oke';
}
}
/**
* @var \App\Entities\Projects\JobsRepository
*/
private $repo;
/**
* Create new Jobs Controller.
*
* @param \App\Entities\Projects\JobsRepository $repo
*/
public function __construct(JobsRepository $repo)
{
$this->repo = $repo;
}
/**
* Show unfinished job list.
*
* @return \Illuminate\View\View
*/
public function index()
{
$user = auth()->user();
if ($user->hasRole('admin')) {
$projects = Project::whereIn('status_id', [2, 3])->pluck('name', 'id');
} else {
$projects = $user->projects()
->whereIn('status_id', [2, 3])
->pluck('projects.name', 'projects.id');
}
$jobs = $this->repo->getUnfinishedJobs($user, request('project_id'));
return view('jobs.unfinished', compact('jobs', 'projects'));
}
/**
* Show a job detail.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function show(Request $request, Job $job)
{
$this->authorize('view', $job);
$editableTask = null;
$editableComment = null;
$comments = $job->comments()->with('creator')->latest()->paginate();
if ($request->get('action') == 'task_edit' && $request->has('task_id')) {
$editableTask = $this->repo->requireTaskById($request->get('task_id'));
}
if ($request->get('action') == 'task_delete' && $request->has('task_id')) {
$editableTask = $this->repo->requireTaskById($request->get('task_id'));
}
if (request('action') == 'comment-edit' && request('comment_id') != null) {
$editableComment = Comment::find(request('comment_id'));
}
return view('jobs.show', compact('job', 'editableTask', 'comments', 'editableComment'));
}
/**
* Show a job edit form.
*
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function edit(Job $job)
{
$files = $job->getMedia();
$this->authorize('view', $job);
$workers = $this->repo->getWorkersList();
return view('jobs.edit', compact('job', 'workers', 'files'));
}
/**
* Update a job on database.
*
* @param \App\Http\Requests\Jobs\UpdateRequest $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\Routing\Redirector
*/
public function update(UpdateRequest $request, Job $job)
{
$job = $this->repo->update($request->except(['_method', '_token']), $job->id);
flash(trans('job.updated'), 'success');
return redirect()->route('jobs.show', $job);
}
/**
* Show job delete confirmation page.
*
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function delete(Job $job)
{
return view('jobs.delete', compact('job'));
}
/**
* Show job delete confirmation page.
*
* @param \App\Http\Requests\Jobs\DeleteRequest $request
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function destroy(DeleteRequest $request, Job $job)
{
$projectId = $job->project_id;
if ($job->id == $request->get('job_id')) {
$job->tasks()->delete();
$job->delete();
flash(trans('job.deleted'), 'success');
} else {
flash(trans('job.undeleted'), 'danger');
}
return redirect()->route('projects.jobs.index', $projectId);
}
/**
* Reorder job task position.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Job $job
* @return string|null
*/
public function tasksReorder(Request $request, Job $job)
{
if ($request->expectsJson()) {
$data = $this->repo->tasksReorder($request->get('postData'));
return 'oke';
}
}
}

241
app/Http/Controllers/Projects/FilesController.php

@ -2,10 +2,10 @@
namespace App\Http\Controllers\Projects;
use File as FileSystem;
use Illuminate\Http\Request;
use App\Entities\Projects\File;
use App\Http\Controllers\Controller;
use File as FileSystem;
use Illuminate\Http\Request;
/**
* Project Files Controller.
@ -14,115 +14,130 @@ use App\Http\Controllers\Controller;
*/
class FilesController extends Controller
{
private $fileableTypes = [
'projects' => 'App\Entities\Projects\Project',
];
public function index(Request $request, $fileableId)
{
$editableFile = null;
$fileableType = $request->segment(1); // projects, jobs
$modelName = $this->getModelName($fileableType);
$modelShortName = $this->getModelShortName($modelName);
$model = $modelName::findOrFail($fileableId);
$files = $model->files;
if (in_array($request->get('action'), ['edit', 'delete']) && $request->has('id')) {
$editableFile = File::find($request->get('id'));
}
return view($fileableType.'.files', [$modelShortName => $model, 'files' => $files, 'editableFile' => $editableFile]);
}
public function create(Request $request, $fileableId)
{
$this->validate($request, [
'fileable_type' => 'required',
'file' => 'required|file|max:10000',
'title' => 'required|max:60',
'description' => 'nullable|max:255',
]);
$fileableType = array_search($request->get('fileable_type'), $this->fileableTypes);
if ($fileableType) {
$file = $this->proccessPhotoUpload($request->except('_token'), $fileableType, $fileableId);
if ($file->exists) {
flash('Upload file berhasil.', 'success');
} else {
flash('Upload file gagal, coba kembali.', 'danger');
}
} else {
flash('Upload file gagal, coba kembali.', 'danger');
}
return back();
}
public function show($fileId)
{
$file = File::find($fileId);
if ($file && file_exists(storage_path('app/public/files/'.$file->filename))) {
$extension = FileSystem::extension('public/files/'.$file->filename);
return response()->download(storage_path('app/public/files/'.$file->filename), $file->title.'.'.$extension);
}
flash(__('file.not_found'), 'danger');
if (\URL::previous() != \URL::current()) {
return back();
}
return redirect()->home();
}
public function update(Request $request, File $file)
{
$file->title = $request->get('title');
$file->description = $request->get('description');
$file->save();
flash(__('file.updated'), 'success');
return redirect()->route($file->fileable_type.'.files', $file->fileable_id);
}
public function destroy(Request $request, File $file)
{
$file->delete();
flash(__('file.deleted'), 'warning');
return redirect()->route($file->fileable_type.'.files', $file->fileable_id);
}
private function proccessPhotoUpload($data, $fileableType, $fileableId)
{
$file = $data['file'];
$fileName = $file->hashName();
$fileData['fileable_id'] = $fileableId;
$fileData['fileable_type'] = $fileableType;
$fileData['filename'] = $fileName;
$fileData['title'] = $data['title'];
$fileData['description'] = $data['description'];
\DB::beginTransaction();
$file->store('public/files');
$file = File::create($fileData);
\DB::commit();
return $file;
}
public function getModelName($fileableType)
{
return isset($this->fileableTypes[$fileableType]) ? $this->fileableTypes[$fileableType] : false;
}
public function getModelShortName($modelName)
{
return strtolower((new \ReflectionClass($modelName))->getShortName());
}
private $fileableTypes = [
'projects' => 'App\Entities\Projects\Project',
];
public function index(Request $request, $fileableId)
{
$editableFile = null;
$fileableType = $request->segment(1); // projects, jobs
$modelName = $this->getModelName($fileableType);
$modelShortName = $this->getModelShortName($modelName);
$model = $modelName::findOrFail($fileableId);
$files = $model->files;
if (in_array($request->get('action'), ['edit', 'delete']) && $request->has('id')) {
$editableFile = File::find($request->get('id'));
}
return view($fileableType . '.files', [$modelShortName => $model, 'files' => $files, 'editableFile' => $editableFile]);
}
public function create(Request $request, $fileableId)
{
$this->validate($request, [
'fileable_type' => 'required',
'file' => 'required|file|max:10000',
'title' => 'required|max:60',
'description' => 'nullable|max:255',
]);
$fileableType = array_search($request->get('fileable_type'), $this->fileableTypes);
if ($fileableType) {
$file = $this->proccessPhotoUpload($request->except('_token'), $fileableType, $fileableId);
if ($file->exists) {
flash('Upload file berhasil.', 'success');
} else {
flash('Upload file gagal, coba kembali.', 'danger');
}
} else {
flash('Upload file gagal, coba kembali.', 'danger');
}
return back();
}
public function show($fileId)
{
$file = File::find($fileId);
if ($file && file_exists(storage_path('app/public/files/' . $file->filename))) {
$extension = FileSystem::extension('public/files/' . $file->filename);
return response()->download(storage_path('app/public/files/' . $file->filename), $file->title . '.' . $extension);
}
flash(__('file.not_found'), 'danger');
if (\URL::previous() != \URL::current()) {
return back();
}
return redirect()->home();
}
public function update(Request $request, File $file)
{
$file->title = $request->get('title');
$file->description = $request->get('description');
$file->save();
flash(__('file.updated'), 'success');
return redirect()->route($file->fileable_type . '.files', $file->fileable_id);
}
public function destroy(Request $request, File $file)
{
$file->delete();
flash(__('file.deleted'), 'warning');
return redirect()->route($file->fileable_type . '.files', $file->fileable_id);
}
private function proccessPhotoUpload($data, $fileableType, $fileableId)
{
$file = $data['file'];
$fileName = $file->hashName();
$fileData['fileable_id'] = $fileableId;
$fileData['fileable_type'] = $fileableType;
$fileData['filename'] = $fileName;
$fileData['title'] = $data['title'];
$fileData['description'] = $data['description'];
\DB::beginTransaction();
$file->store('public/files');
$file = File::create($fileData);
\DB::commit();
return $file;
}
public function getModelName($fileableType)
{
return isset($this->fileableTypes[$fileableType]) ? $this->fileableTypes[$fileableType] : false;
}
public function getModelShortName($modelName)
{
return strtolower((new \ReflectionClass($modelName))->getShortName());
}
public function showAttachment(Request $request, \App\Entities\Projects\Job $job, $media_id)
{
$files = $job->getMedia();
foreach ($files as $file) {
if ($file->id == $media_id) {
return \Response::file($file->getPath());
}
}
return back()->withErrors(['media not found']);
}
}

150
config/medialibrary.php

@ -0,0 +1,150 @@
<?php
return [
/*
* The disk on which to store added files and derived images by default. Choose
* one or more of the disks you've configured in config/filesystems.php.
*/
'disk_name' => 'public',
/*
* The maximum file size of an item in bytes.
* Adding a larger file will result in an exception.
*/
'max_file_size' => 1024 * 1024 * 10,
/*
* This queue will be used to generate derived and responsive images.
* Leave empty to use the default queue.
*/
'queue_name' => '',
/*
* The fully qualified class name of the media model.
*/
'media_model' => Spatie\MediaLibrary\Models\Media::class,
's3' => [
/*
* The domain that should be prepended when generating urls.
*/
'domain' => 'https://'.env('AWS_BUCKET').'.s3.amazonaws.com',
],
'remote' => [
/*
* Any extra headers that should be included when uploading media to
* a remote disk. Even though supported headers may vary between
* different drivers, a sensible default has been provided.
*
* Supported by S3: CacheControl, Expires, StorageClass,
* ServerSideEncryption, Metadata, ACL, ContentEncoding
*/
'extra_headers' => [
'CacheControl' => 'max-age=604800',
],
],
'responsive_images' => [
/*
* This class is responsible for calculating the target widths of the responsive
* images. By default we optimize for filesize and create variations that each are 20%
* smaller than the previous one. More info in the documentation.
*
* https://docs.spatie.be/laravel-medialibrary/v7/advanced-usage/generating-responsive-images
*/
'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class,
/*
* By default rendering media to a responsive image will add some javascript and a tiny placeholder.
* This ensures that the browser can already determine the correct layout.
*/
'use_tiny_placeholders' => true,
/*
* This class will generate the tiny placeholder used for progressive image loading. By default
* the medialibrary will use a tiny blurred jpg image.
*/
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
],
/*
* When urls to files get generated, this class will be called. Leave empty
* if your files are stored locally above the site root or on s3.
*/
'url_generator' => null,
/*
* The class that contains the strategy for determining a media file's path.
*/
'path_generator' => null,
/*
* Medialibrary will try to optimize all converted images by removing
* metadata and applying a little bit of compression. These are
* the optimizers that will be used by default.
*/
'image_optimizers' => [
Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
'--strip-all', // this strips out all text information such as comments and EXIF data
'--all-progressive', // this will make sure the resulting image is a progressive one
],
Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
'--force', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Optipng::class => [
'-i0', // this will result in a non-interlaced, progressive scanned image
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
'-quiet', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Svgo::class => [
'--disable=cleanupIDs', // disabling because it is known to cause troubles
],
Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [
'-b', // required parameter for this package
'-O3', // this produces the slowest but best results
],
],
/*
* These generators will be used to create an image of media files.
*/
'image_generators' => [
Spatie\MediaLibrary\ImageGenerators\FileTypes\Image::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Webp::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Pdf::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Svg::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Video::class,
],
/*
* The engine that should perform the image conversions.
* Should be either `gd` or `imagick`.
*/
'image_driver' => 'gd',
/*
* FFMPEG & FFProbe binaries paths, only used if you try to generate video
* thumbnails and have installed the php-ffmpeg/php-ffmpeg composer
* dependency.
*/
'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
/*
* The path where to store temporary files while performing image conversions.
* If set to null, storage_path('medialibrary/temp') will be used.
*/
'temporary_directory_path' => null,
/*
* Here you can override the class names of the jobs used by this package. Make sure
* your custom jobs extend the ones provided by the package.
*/
'jobs' => [
'perform_conversions' => Spatie\MediaLibrary\Jobs\PerformConversions::class,
'generate_responsive_images' => Spatie\MediaLibrary\Jobs\GenerateResponsiveImages::class,
],
];

38
database/migrations/2019_06_29_145856_create_media_table.php

@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMediaTable extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('media', function (Blueprint $table) {
$table->increments('id');
$table->morphs('model');
$table->string('collection_name');
$table->string('name');
$table->string('file_name');
$table->string('mime_type')->nullable();
$table->string('disk');
$table->unsignedInteger('size');
$table->json('manipulations');
$table->json('custom_properties');
$table->json('responsive_images');
$table->unsignedInteger('order_column')->nullable();
$table->nullableTimestamps();
});
}
/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('media');
}
}

68
resources/lang/de/auth.php

@ -1,43 +1,43 @@
<?php
return [
// Profile
'profile' => 'Mein Profil',
'profile_edit' => 'Mein Profil bearbeiten',
'update_profile' => 'Profil aktualisieren',
'profile_updated' => 'Profil wurde aktualisiert.',
// Profile
'profile' => 'Mein Profil',
'profile_edit' => 'Mein Profil bearbeiten',
'update_profile' => 'Profil aktualisieren',
'profile_updated' => 'Profil wurde aktualisiert.',
// Registration
'register' => 'Neuen Account anlegen',
'need_account' => 'Account benötigt?',
'have_an_account' => 'Ich habe bereits einen Account',
// Registration
'register' => 'Neuen Account anlegen',
'need_account' => 'Account benötigt?',
'have_an_account' => 'Ich habe bereits einen Account',
// Login & Logout
'login' => 'Login',
'welcome' => 'Willkommen :name.',
'failed' => 'Diese Zugangsdaten passen nicht zu unseren Daten.',
'throttle' => 'Zu viele Login Versuche. Bitte probiere es erneut in :seconds Sekunden.',
'logout' => 'Logout',
'logged_out' => 'Sie haben sich ausgeloggt.',
'remember_me' => 'Erinnere dich an mich',
// Login & Logout
'login' => 'Login',
'welcome' => 'Willkommen :name.',
'failed' => 'Diese Zugangsdaten passen nicht zu unseren Daten.',
'throttle' => 'Zu viele Login Versuche. Bitte probiere es erneut in :seconds Sekunden.',
'logout' => 'Logout',
'logged_out' => 'Sie haben sich ausgeloggt.',
'remember_me' => 'Erinnere dich an mich',
// Password
'change_password' => 'Passwort ändern',
'password_changed' => 'Ihr Passwort wurde geändert',
'forgot_password' => 'Passwort vergessen?',
'reset_password' => 'Password zurücksetzen',
'send_reset_password_link' => 'Sende Passwort Reset Link',
'old_password_failed' => 'Altes Passwort stimmt nicht überein!',
'reset_password_hint' => 'Bitte setzen Sie Ihr Passwort zurück, indem Sie dieses Formular ausfüllen',
// Password
'change_password' => 'Passwort ändern',
'password_changed' => 'Ihr Passwort wurde geändert',
'forgot_password' => 'Passwort vergessen?',
'reset_password' => 'Password zurücksetzen',
'send_reset_password_link' => 'Sende Passwort Reset Link',
'old_password_failed' => 'Altes Passwort stimmt nicht überein!',
'reset_password_hint' => 'Bitte setzen Sie Ihr Passwort zurück, indem Sie dieses Formular ausfüllen',
// Attributes
'email' => 'E-Mail',
'password' => 'Passwort',
'password_confirmation' => 'Passwort bestätigen',
'old_password' => 'Altes Passwort',
'new_password' => 'Neues Passwort',
'new_password_confirmation' => 'Neues Passwort bestätigen',
// Attributes
'email' => 'E-Mail',
'password' => 'Passwort',
'password_confirmation' => 'Passwort bestätigen',
'old_password' => 'Altes Passwort',
'new_password' => 'Neues Passwort',
'new_password_confirmation' => 'Neues Passwort bestätigen',
// Authorization
'unauthorized_access' => 'Sie können nicht auf die Seite :url zugreifen.',
// Authorization
'unauthorized_access' => 'Sie können nicht auf die Seite :url zugreifen.',
];

93
resources/lang/de/job.php

@ -1,53 +1,54 @@
<?php
return [
// Labels
'job' => 'Beschäftigung',
'list' => 'Beschäftigungsliste',
'on_progress' => 'in Arbeit',
'detail' => 'Beschäftigungsdetails',
'search' => 'Beschäftigung suchen',
'found' => 'Beschäftigung gefunden.',
'not_found' => 'Beschäftigung nicht gefunden.',
'tasks' => 'Aufgabenliste',
'price_total' => 'Beschäftigungsgesamtpreis',
'tasks_count' => 'Beschäftigungsanzahl',
'empty' => 'Beschäftigungsliste ist leer.',
'back_to_index' => 'zurück zur Beschäftigungsliste',
'starts' => 'Starts',
'ends' => 'Ends',
'target' => 'Target',
'actual' => 'Actual',
'duration' => 'Duration',
// Labels
'job' => 'Beschäftigung',
'list' => 'Beschäftigungsliste',
'on_progress' => 'in Arbeit',
'detail' => 'Beschäftigungsdetails',
'search' => 'Beschäftigung suchen',
'found' => 'Beschäftigung gefunden.',
'not_found' => 'Beschäftigung nicht gefunden.',
'tasks' => 'Aufgabenliste',
'price_total' => 'Beschäftigungsgesamtpreis',
'tasks_count' => 'Beschäftigungsanzahl',
'empty' => 'Beschäftigungsliste ist leer.',
'back_to_index' => 'zurück zur Beschäftigungsliste',
'starts' => 'Starts',
'ends' => 'Ends',
'target' => 'Target',
'actual' => 'Actual',
'duration' => 'Duration',
// Actions
'create' => 'Neue Beschäftigung anlegen',
'add' => 'Beschäftigung hinzufügen',
'created' => 'Neue Beschäftigung wurde angelegt.',
'show' => 'Beschäftigungsdetails anzeigen',
'edit' => 'Beschäftigung bearbeiten',
'update' => 'Beschäftigung aktualisieren',
'updated' => 'Beschäftigungsdetails wurden aktualisiert.',
'delete' => 'Beschäftigung löschen',
'deleted' => 'Beschäftigung wurde gelöscht.',
'undeleted' => 'Beschäftigung nicht gelöscht.',
'add_from_other_project' => 'Beschäftigung von einem anderen Projekt hinzufügen',
'select_project' => 'ein Projekt auswählen.',
'sort_tasks' => 'sortiere Aufgabenpriorität',
// Actions
'create' => 'Neue Beschäftigung anlegen',
'add' => 'Beschäftigung hinzufügen',
'created' => 'Neue Beschäftigung wurde angelegt.',
'show' => 'Beschäftigungsdetails anzeigen',
'edit' => 'Beschäftigung bearbeiten',
'update' => 'Beschäftigung aktualisieren',
'updated' => 'Beschäftigungsdetails wurden aktualisiert.',
'delete' => 'Beschäftigung löschen',
'deleted' => 'Beschäftigung wurde gelöscht.',
'undeleted' => 'Beschäftigung nicht gelöscht.',
'add_from_other_project' => 'Beschäftigung von einem anderen Projekt hinzufügen',
'select_project' => 'ein Projekt auswählen.',
'sort_tasks' => 'sortiere Aufgabenpriorität',
// Attributes
'name' => 'Beschäftigungsname',
'description' => 'Beschreibung',
'progress' => 'Fortschritt',
'worker' => 'Bearbeiter',
'price' => 'Preis',
'type' => 'Beschäftigungstyp',
'target_start_date' => 'Target Start Date',
'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date',
// Attributes
'name' => 'Beschäftigungsname',
'description' => 'Beschreibung',
'progress' => 'Fortschritt',
'worker' => 'Bearbeiter',
'price' => 'Preis',
'type' => 'Beschäftigungstyp',
'target_start_date' => 'Target Start Date',
'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date',
'files' => 'An diesen Job angehängte Dateien',
// Types
'main' => 'Haupt',
'additional' => 'Zusätzlich',
// Types
'main' => 'Haupt',
'additional' => 'Zusätzlich',
];

68
resources/lang/en/auth.php

@ -1,43 +1,43 @@
<?php
return [
// Profile
'profile' => 'My Profile',
'profile_edit' => 'Edit My Profile',
'update_profile' => 'Update Profile',
'profile_updated' => 'Profile has been updated.',
// Profile
'profile' => 'My Profile',
'profile_edit' => 'Edit My Profile',
'update_profile' => 'Update Profile',
'profile_updated' => 'Profile has been updated.',
// Registration
'register' => 'Create new Account',
'need_account' => 'Need an Account?',
'have_an_account' => 'I have an Account',
// Registration
'register' => 'Create new Account',
'need_account' => 'Need an Account?',
'have_an_account' => 'I have an Account',
// Login & Logout
'login' => 'Login',
'welcome' => 'Welcome :name.',
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'logout' => 'Logout',
'logged_out' => 'You have logged out.',
'remember_me' => 'Remember me',
// Login & Logout
'login' => 'Login',
'welcome' => 'Welcome :name.',
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'logout' => 'Logout',
'logged_out' => 'You have logged out.',
'remember_me' => 'Remember me',
// Password
'change_password' => 'Change Password',
'password_changed' => 'Your password has been changed.',
'forgot_password' => 'Forgot Password?',
'reset_password' => 'Reset Password',
'send_reset_password_link' => 'Send reset password link',
'old_password_failed' => 'Old password does not match!',
'reset_password_hint' => 'Please reset your password by filling out this form',
// Password
'change_password' => 'Change Password',
'password_changed' => 'Your password has been changed.',
'forgot_password' => 'Forgot Password?',
'reset_password' => 'Reset Password',
'send_reset_password_link' => 'Send reset password link',
'old_password_failed' => 'Old password does not match!',
'reset_password_hint' => 'Please reset your password by filling out this form',
// Attributes
'email' => 'Email',
'password' => 'Password',
'password_confirmation' => 'Confirm Password',
'old_password' => 'Old Password',
'new_password' => 'New Password',
'new_password_confirmation' => 'Confirm new Password',
// Attributes
'email' => 'Email',
'password' => 'Password',
'password_confirmation' => 'Confirm Password',
'old_password' => 'Old Password',
'new_password' => 'New Password',
'new_password_confirmation' => 'Confirm new Password',
// Authorization
'unauthorized_access' => 'You cannot access :url page.',
// Authorization
'unauthorized_access' => 'You cannot access :url page.',
];

95
resources/lang/en/job.php

@ -1,55 +1,56 @@
<?php
return [
// Labels
'job' => 'Job',
'list' => 'Job List',
'on_progress' => 'Job on Progress',
'detail' => 'Job Detail',
'search' => 'Ssearch Job',
'found' => 'Job found.',
'not_found' => 'Job not found.',
'tasks' => 'Task List',
'price_total' => 'Job Price Total',
'tasks_count' => 'Tasks Count',
'empty' => 'Job list is empty.',
'back_to_index' => 'Back to Job List',
'starts' => 'Starts',
'ends' => 'Ends',
'target' => 'Target',
'actual' => 'Actual',
'duration' => 'Duration',
// Labels
'job' => 'Job',
'list' => 'Job List',
'on_progress' => 'Job on Progress',
'detail' => 'Job Detail',
'search' => 'Ssearch Job',
'found' => 'Job found.',
'not_found' => 'Job not found.',
'tasks' => 'Task List',
'price_total' => 'Job Price Total',
'tasks_count' => 'Tasks Count',
'empty' => 'Job list is empty.',
'back_to_index' => 'Back to Job List',
'starts' => 'Starts',
'ends' => 'Ends',
'target' => 'Target',
'actual' => 'Actual',
'duration' => 'Duration',
// Actions
'create' => 'Create new Job',
'add' => 'Add Jobs',
'created' => 'New Job has been created.',
'show' => 'View Job Detail',
'edit' => 'Edit Job',
'update' => 'Update Job',
'updated' => 'Job data has been updated.',
'delete' => 'Delete Job',
'deleted' => 'Job has been deleted.',
'undeleted' => 'Job not deleted.',
'add_from_other_project' => 'Add Job from another Project',
'select_project' => 'Select a project.',
'sort_tasks' => 'Sort Task Priority',
// Actions
'create' => 'Create new Job',
'add' => 'Add Jobs',
'created' => 'New Job has been created.',
'show' => 'View Job Detail',
'edit' => 'Edit Job',
'update' => 'Update Job',
'updated' => 'Job data has been updated.',
'delete' => 'Delete Job',
'deleted' => 'Job has been deleted.',
'undeleted' => 'Job not deleted.',
'add_from_other_project' => 'Add Job from another Project',
'select_project' => 'Select a project.',
'sort_tasks' => 'Sort Task Priority',
'created_from_other_project' => 'Job has been added from other Project.',
'created_from_other_project' => 'Job has been added from other Project.',
// Attributes
'name' => 'Job Name',
'description' => 'Description',
'progress' => 'Progress',
'worker' => 'Worker',
'price' => 'Price',
'type' => 'Job Type',
'target_start_date' => 'Target Start Date',
'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date',
// Attributes
'name' => 'Job Name',
'description' => 'Description',
'progress' => 'Progress',
'worker' => 'Worker',
'price' => 'Price',
'type' => 'Job Type',
'target_start_date' => 'Target Start Date',
'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date',
'files' => 'Files attached to this job',
// Types
'main' => 'Main',
'additional' => 'Additional',
// Types
'main' => 'Main',
'additional' => 'Additional',
];

68
resources/lang/id/auth.php

@ -1,43 +1,43 @@
<?php
return [
// Profile
'profile' => 'Profil Saya',
'profile_edit' => 'Edit Profil Saya',
'update_profile' => 'Update Profil',
'profile_updated' => 'Profil sudah diupdate.',
// Profile
'profile' => 'Profil Saya',
'profile_edit' => 'Edit Profil Saya',
'update_profile' => 'Update Profil',
'profile_updated' => 'Profil sudah diupdate.',
// Registration
'register' => 'Buat Akun Baru',
'need_account' => 'Belum punya Akun?',
'have_an_account' => 'Saya sudah punya Akun',
// Registration
'register' => 'Buat Akun Baru',
'need_account' => 'Belum punya Akun?',
'have_an_account' => 'Saya sudah punya Akun',
// Login & Logout
'login' => 'Login',
'welcome' => 'Selamat datang kembali :name.',
'failed' => 'Identitas tersebut tidak cocok dengan data kami.',
'throttle' => 'Terlalu banyak usaha masuk. Silahkan coba lagi dalam :seconds detik.',
'logout' => 'Keluar',
'logged_out' => 'Anda telah logout.',
'remember_me' => 'Ingat saya',
// Login & Logout
'login' => 'Login',
'welcome' => 'Selamat datang kembali :name.',
'failed' => 'Identitas tersebut tidak cocok dengan data kami.',
'throttle' => 'Terlalu banyak usaha masuk. Silahkan coba lagi dalam :seconds detik.',
'logout' => 'Keluar',
'logged_out' => 'Anda telah logout.',
'remember_me' => 'Ingat saya',
// Password
'change_password' => 'Ganti Password',
'password_changed' => 'Password berhasil diubah.',
'forgot_password' => 'Lupa Password?',
'reset_password' => 'Reset Password',
'send_reset_password_link' => 'Kirim Link Reset Password',
'old_password_failed' => 'Password lama tidak cocok!',
'reset_password_hint' => 'Silakan melakukan reset password dengan mengisi form berikut',
// Password
'change_password' => 'Ganti Password',
'password_changed' => 'Password berhasil diubah.',
'forgot_password' => 'Lupa Password?',
'reset_password' => 'Reset Password',
'send_reset_password_link' => 'Kirim Link Reset Password',
'old_password_failed' => 'Password lama tidak cocok!',
'reset_password_hint' => 'Silakan melakukan reset password dengan mengisi form berikut',
// Attributes
'email' => 'Email',
'password' => 'Password',
'password_confirmation' => 'Ulangi Password',
'old_password' => 'Password Lama',
'new_password' => 'Password Baru',
'new_password_confirmation' => 'Ulangi Password Baru',
// Attributes
'email' => 'Email',
'password' => 'Password',
'password_confirmation' => 'Ulangi Password',
'old_password' => 'Password Lama',
'new_password' => 'Password Baru',
'new_password_confirmation' => 'Ulangi Password Baru',
// Authorization
'unauthorized_access' => 'Anda tidak dapat mengakses halaman :url.',
// Authorization
'unauthorized_access' => 'Anda tidak dapat mengakses halaman :url.',
];

95
resources/lang/id/job.php

@ -1,55 +1,56 @@
<?php
return [
// Labels
'job' => 'Job',
'list' => 'Daftar Job',
'on_progress' => 'Job on Progress',
'detail' => 'Detail Job',
'search' => 'Cari Job',
'found' => 'Job ditemukan',
'not_found' => 'Job tidak ditemukan',
'tasks' => 'Daftar Task',
'price_total' => 'Nilai Job Total',
'tasks_count' => 'Jumlah Task',
'empty' => 'Belum ada Job',
'back_to_index' => 'Kembali ke daftar Job',
'starts' => 'Mulai',
'ends' => 'Selesai',
'target' => 'Target',
'actual' => 'Aktual',
'duration' => 'Durasi',
// Labels
'job' => 'Job',
'list' => 'Daftar Job',
'on_progress' => 'Job on Progress',
'detail' => 'Detail Job',
'search' => 'Cari Job',
'found' => 'Job ditemukan',
'not_found' => 'Job tidak ditemukan',
'tasks' => 'Daftar Task',
'price_total' => 'Nilai Job Total',
'tasks_count' => 'Jumlah Task',
'empty' => 'Belum ada Job',
'back_to_index' => 'Kembali ke daftar Job',
'starts' => 'Mulai',
'ends' => 'Selesai',
'target' => 'Target',
'actual' => 'Aktual',
'duration' => 'Durasi',
// Actions
'create' => 'Input Job Baru',
'add' => 'Tambahkan Job',
'created' => 'Input Job baru telah berhasil.',
'show' => 'Detail Job',
'edit' => 'Edit Job',
'update' => 'Update Job',
'updated' => 'Update data Job telah berhasil.',
'delete' => 'Hapus Job',
'deleted' => 'Hapus data Job telah berhasil.',
'undeleted' => 'Data Job gagal dihapus.',
'add_from_other_project' => 'Tambah Job dari Project Lain',
'select_project' => 'Pilih salah satu project.',
'sort_tasks' => 'Urutkan Prioritas Task',
// Actions
'create' => 'Input Job Baru',
'add' => 'Tambahkan Job',
'created' => 'Input Job baru telah berhasil.',
'show' => 'Detail Job',
'edit' => 'Edit Job',
'update' => 'Update Job',
'updated' => 'Update data Job telah berhasil.',
'delete' => 'Hapus Job',
'deleted' => 'Hapus data Job telah berhasil.',
'undeleted' => 'Data Job gagal dihapus.',
'add_from_other_project' => 'Tambah Job dari Project Lain',
'select_project' => 'Pilih salah satu project.',
'sort_tasks' => 'Urutkan Prioritas Task',
'created_from_other_project' => 'Berhasil tambah job dari Project lain.',
'created_from_other_project' => 'Berhasil tambah job dari Project lain.',
// Attributes
'name' => 'Nama Job',
'description' => 'Deskripsi',
'progress' => 'Progress',
'worker' => 'Pekerja',
'price' => 'Biaya Pengerjaan',
'type' => 'Jenis Job',
'target_start_date' => 'Target Tgl Mulai',
'target_end_date' => 'Target Tgl Selesai',
'actual_start_date' => 'Tgl Mulai Aktual',
'actual_end_date' => 'Tgl Selesai Aktual',
// Attributes
'name' => 'Nama Job',
'description' => 'Deskripsi',
'progress' => 'Progress',
'worker' => 'Pekerja',
'price' => 'Biaya Pengerjaan',
'type' => 'Jenis Job',
'target_start_date' => 'Target Tgl Mulai',
'target_end_date' => 'Target Tgl Selesai',
'actual_start_date' => 'Tgl Mulai Aktual',
'actual_end_date' => 'Tgl Selesai Aktual',
'files' => 'File terlampir pada pekerjaan ini',
// Types
'main' => 'Utama',
'additional' => 'Tambahan',
// Types
'main' => 'Utama',
'additional' => 'Tambahan',
];

12
resources/views/jobs/edit.blade.php

@ -5,7 +5,7 @@
@section('content')
<div class="row"><br>
<div class="col-md-6">
{!! Form::model($job, ['route' => ['jobs.update', $job], 'method' => 'patch']) !!}
{!! Form::model($job, ['route' => ['jobs.update', $job], 'method' => 'patch' , 'files' => true] ) !!}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ $job->name }} <small>{{ __('job.edit') }}</small></h3></div>
<div class="panel-body">
@ -30,6 +30,16 @@
<div class="col-md-4">{!! FormField::text('actual_end_date', ['label' => __('job.actual_end_date'), 'class' => 'date-select']) !!}</div>
</div>
{!! FormField::textarea('description', ['label' => __('job.description') , 'class' => 'countable']) !!}
@if ($files->isNotEmpty())
<b>
{{$files->count()}} files
</b>
@else
no files attached yet
@endif
{!! Form::file('files[]', ['label' => __('job.files') , 'multiple' => 'true']) !!}
</div>
<div class="panel-footer">

11
resources/views/jobs/partials/job-show.blade.php

@ -11,6 +11,17 @@
<tr><th>{{ __('job.worker') }}</th><td>{{ $job->worker->name }}</td></tr>
<tr><th>{{ __('time.updated_at') }}</th><td>{{ $job->updated_at }}</td></tr>
<tr><th>{{ __('job.description') }}</th><td>{!! nl2br($job->description) !!}</td></tr>
<tr>
<th>{{ __('job.files') }}</th>
<td>
@if ($job->hasMedia())
@foreach ($job->getMedia() as $item)
<a target="_blank" href="{{route('show-job-file', ['job' => $job->id , 'media_id' => $item->id])}}">{{$item->name}}</a>
@endforeach
@else
@endif
</td>
</tr>
</tbody>
</table>
</div>

193
routes/web/projects.php

@ -1,104 +1,109 @@
<?php
Route::group(['middleware' => ['auth'], 'namespace' => 'Projects'], function () {
/*
* Projects Routes
*/
Route::get('projects/{project}/delete', ['as' => 'projects.delete', 'uses' => 'ProjectsController@delete']);
Route::get('projects/{project}/subscriptions', ['as' => 'projects.subscriptions', 'uses' => 'ProjectsController@subscriptions']);
Route::patch('projects/{project}/status-update', ['as' => 'projects.status-update', 'uses' => 'ProjectsController@statusUpdate']);
Route::resource('projects', 'ProjectsController');
/*
* Project Payments Routes
*/
Route::get('projects/{project}/payments', ['as' => 'projects.payments', 'uses' => 'ProjectsController@payments']);
/*
* Project Fees Routes
*/
Route::get('projects/{project}/fees/create', ['as' => 'projects.fees.create', 'uses' => 'FeesController@create']);
Route::post('projects/{project}/fees/store', ['as' => 'projects.fees.store', 'uses' => 'FeesController@store']);
/*
* Project Invoices Routes
*/
Route::get('projects/{project}/invoices', ['as' => 'projects.invoices', 'uses' => 'InvoicesController@index']);
/*
* Project Jobs Routes
*/
Route::get('projects/{project}/jobs-export/{type?}', ['as' => 'projects.jobs-export', 'uses' => 'JobsController@jobsExport']);
Route::get('projects/{project}/job-progress-export/{type?}', ['as' => 'projects.job-progress-export', 'uses' => 'JobsController@jobProgressExport']);
Route::get('projects/{project}/jobs/create', ['as' => 'projects.jobs.create', 'uses' => 'JobsController@create']);
Route::post('projects/{project}/jobs', ['as' => 'projects.jobs.store', 'uses' => 'JobsController@store']);
Route::get('projects/{project}/jobs/add-from-other-project', ['as' => 'projects.jobs.add-from-other-project', 'uses' => 'JobsController@addFromOtherProject']);
Route::post('projects/{project}/jobs/store-from-other-project', ['as' => 'projects.jobs.store-from-other-project', 'uses' => 'JobsController@storeFromOtherProject']);
Route::get('projects/{project}/jobs', ['as' => 'projects.jobs.index', 'uses' => 'JobsController@index']);
Route::post('projects/{project}/jobs-reorder', ['as' => 'projects.jobs-reorder', 'uses' => 'ProjectsController@jobsReorder']);
/*
* Project Comments Routes
*/
Route::get('projects/{project}/comments', 'CommentsController@index')->name('projects.comments.index');
Route::post('projects/{project}/comments', 'CommentsController@store')->name('projects.comments.store');
Route::patch('projects/{project}/comments/{comment}', 'CommentsController@update')->name('projects.comments.update');
Route::delete('projects/{project}/comments/{comment}', 'CommentsController@destroy')->name('projects.comments.destroy');
/*
* Project Issues Routes
*/
Route::get('projects/{project}/issues', 'IssueController@index')->name('projects.issues.index');
Route::get('projects/{project}/issues/create', 'IssueController@create')->name('projects.issues.create');
Route::post('projects/{project}/issues', 'IssueController@store')->name('projects.issues.store');
Route::get('projects/{project}/issues/{issue}', 'IssueController@show')->name('projects.issues.show');
Route::get('projects/{project}/issues/{issue}/edit', 'IssueController@edit')->name('projects.issues.edit');
Route::patch('projects/{project}/issues/{issue}', 'IssueController@update')->name('projects.issues.update');
Route::delete('projects/{project}/issues/{issue}', 'IssueController@destroy')->name('projects.issues.destroy');
/*
* Tasks Routes
*/
Route::get('jobs/{job}/tasks/create', ['as' => 'tasks.create', 'uses' => 'TasksController@create']);
Route::post('jobs/{job}/tasks', ['as' => 'tasks.store', 'uses' => 'TasksController@store']);
Route::patch('tasks/{task}', ['as' => 'tasks.update', 'uses' => 'TasksController@update']);
Route::delete('tasks/{task}', ['as' => 'tasks.destroy', 'uses' => 'TasksController@destroy']);
Route::post('tasks/{task}/set-as-job', ['as' => 'tasks.set-as-job', 'uses' => 'TasksController@setAsJob']);
/*
* Files Routes
*/
Route::get('projects/{project}/files', ['as' => 'projects.files', 'uses' => 'FilesController@index']);
Route::post('files/{fileable}', ['as' => 'files.upload', 'uses' => 'FilesController@create']);
Route::get('files/{file}', ['as' => 'files.download', 'uses' => 'FilesController@show']);
Route::patch('files/{file}', ['as' => 'files.update', 'uses' => 'FilesController@update']);
Route::delete('files/{file}', ['as' => 'files.destroy', 'uses' => 'FilesController@destroy']);
/*
* Projects Routes
*/
Route::get('projects/{project}/delete', ['as' => 'projects.delete', 'uses' => 'ProjectsController@delete']);
Route::get('projects/{project}/subscriptions', ['as' => 'projects.subscriptions', 'uses' => 'ProjectsController@subscriptions']);
Route::patch('projects/{project}/status-update', ['as' => 'projects.status-update', 'uses' => 'ProjectsController@statusUpdate']);
Route::resource('projects', 'ProjectsController');
/*
* Project Payments Routes
*/
Route::get('projects/{project}/payments', ['as' => 'projects.payments', 'uses' => 'ProjectsController@payments']);
/*
* Project Fees Routes
*/
Route::get('projects/{project}/fees/create', ['as' => 'projects.fees.create', 'uses' => 'FeesController@create']);
Route::post('projects/{project}/fees/store', ['as' => 'projects.fees.store', 'uses' => 'FeesController@store']);
/*
* Project Invoices Routes
*/
Route::get('projects/{project}/invoices', ['as' => 'projects.invoices', 'uses' => 'InvoicesController@index']);
/*
* Project Jobs Routes
*/
Route::get('projects/{project}/jobs-export/{type?}', ['as' => 'projects.jobs-export', 'uses' => 'JobsController@jobsExport']);
Route::get('projects/{project}/job-progress-export/{type?}', ['as' => 'projects.job-progress-export', 'uses' => 'JobsController@jobProgressExport']);
Route::get('projects/{project}/jobs/create', ['as' => 'projects.jobs.create', 'uses' => 'JobsController@create']);
Route::post('projects/{project}/jobs', ['as' => 'projects.jobs.store', 'uses' => 'JobsController@store']);
Route::get('projects/{project}/jobs/add-from-other-project', ['as' => 'projects.jobs.add-from-other-project', 'uses' => 'JobsController@addFromOtherProject']);
Route::post('projects/{project}/jobs/store-from-other-project', ['as' => 'projects.jobs.store-from-other-project', 'uses' => 'JobsController@storeFromOtherProject']);
Route::get('projects/{project}/jobs', ['as' => 'projects.jobs.index', 'uses' => 'JobsController@index']);
Route::post('projects/{project}/jobs-reorder', ['as' => 'projects.jobs-reorder', 'uses' => 'ProjectsController@jobsReorder']);
/*
* Project Comments Routes
*/
Route::get('projects/{project}/comments', 'CommentsController@index')->name('projects.comments.index');
Route::post('projects/{project}/comments', 'CommentsController@store')->name('projects.comments.store');
Route::patch('projects/{project}/comments/{comment}', 'CommentsController@update')->name('projects.comments.update');
Route::delete('projects/{project}/comments/{comment}', 'CommentsController@destroy')->name('projects.comments.destroy');
/*
* Project Issues Routes
*/
Route::get('projects/{project}/issues', 'IssueController@index')->name('projects.issues.index');
Route::get('projects/{project}/issues/create', 'IssueController@create')->name('projects.issues.create');
Route::post('projects/{project}/issues', 'IssueController@store')->name('projects.issues.store');
Route::get('projects/{project}/issues/{issue}', 'IssueController@show')->name('projects.issues.show');
Route::get('projects/{project}/issues/{issue}/edit', 'IssueController@edit')->name('projects.issues.edit');
Route::patch('projects/{project}/issues/{issue}', 'IssueController@update')->name('projects.issues.update');
Route::delete('projects/{project}/issues/{issue}', 'IssueController@destroy')->name('projects.issues.destroy');
/*
* Tasks Routes
*/
Route::get('jobs/{job}/tasks/create', ['as' => 'tasks.create', 'uses' => 'TasksController@create']);
Route::post('jobs/{job}/tasks', ['as' => 'tasks.store', 'uses' => 'TasksController@store']);
Route::patch('tasks/{task}', ['as' => 'tasks.update', 'uses' => 'TasksController@update']);
Route::delete('tasks/{task}', ['as' => 'tasks.destroy', 'uses' => 'TasksController@destroy']);
Route::post('tasks/{task}/set-as-job', ['as' => 'tasks.set-as-job', 'uses' => 'TasksController@setAsJob']);
/*
* Files Routes
*/
Route::get('projects/{project}/files', ['as' => 'projects.files', 'uses' => 'FilesController@index']);
Route::post('files/{fileable}', ['as' => 'files.upload', 'uses' => 'FilesController@create']);
Route::get('files/{file}', ['as' => 'files.download', 'uses' => 'FilesController@show']);
Route::patch('files/{file}', ['as' => 'files.update', 'uses' => 'FilesController@update']);
Route::delete('files/{file}', ['as' => 'files.destroy', 'uses' => 'FilesController@destroy']);
/**
* Job file attachments
*/
Route::get('files/show/{job}/{media_id}', 'FilesController@showAttachment')->name('show-job-file');
});
Route::group(['middleware' => ['auth']], function () {
/*
* Jobs Routes
*/
Route::get('jobs', ['as' => 'jobs.index', 'uses' => 'JobsController@index']);
Route::get('jobs/{job}', ['as' => 'jobs.show', 'uses' => 'JobsController@show']);
/*
* Job Actions Routes
*/
Route::get('jobs/{job}/edit', ['as' => 'jobs.edit', 'uses' => 'JobsController@edit']);
Route::patch('jobs/{job}', ['as' => 'jobs.update', 'uses' => 'JobsController@update']);
Route::get('jobs/{job}/delete', ['as' => 'jobs.delete', 'uses' => 'JobsController@delete']);
Route::delete('jobs/{job}', ['as' => 'jobs.destroy', 'uses' => 'JobsController@destroy']);
Route::post('jobs/{id}/tasks-reorder', ['as' => 'jobs.tasks-reorder', 'uses' => 'JobsController@tasksReorder']);
/*
* Project Comments Routes
*/
Route::get('jobs/{job}/comments', 'Jobs\CommentsController@index')->name('jobs.comments.index');
Route::post('jobs/{job}/comments', 'Jobs\CommentsController@store')->name('jobs.comments.store');
Route::patch('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@update')->name('jobs.comments.update');
Route::delete('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@destroy')->name('jobs.comments.destroy');
/*
* Jobs Routes
*/
Route::get('jobs', ['as' => 'jobs.index', 'uses' => 'JobsController@index']);
Route::get('jobs/{job}', ['as' => 'jobs.show', 'uses' => 'JobsController@show']);
/*
* Job Actions Routes
*/
Route::get('jobs/{job}/edit', ['as' => 'jobs.edit', 'uses' => 'JobsController@edit']);
Route::patch('jobs/{job}', ['as' => 'jobs.update', 'uses' => 'JobsController@update']);
Route::get('jobs/{job}/delete', ['as' => 'jobs.delete', 'uses' => 'JobsController@delete']);
Route::delete('jobs/{job}', ['as' => 'jobs.destroy', 'uses' => 'JobsController@destroy']);
Route::post('jobs/{id}/tasks-reorder', ['as' => 'jobs.tasks-reorder', 'uses' => 'JobsController@tasksReorder']);
/*
* Project Comments Routes
*/
Route::get('jobs/{job}/comments', 'Jobs\CommentsController@index')->name('jobs.comments.index');
Route::post('jobs/{job}/comments', 'Jobs\CommentsController@store')->name('jobs.comments.store');
Route::patch('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@update')->name('jobs.comments.update');
Route::delete('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@destroy')->name('jobs.comments.destroy');
});
/*

Loading…
Cancel
Save