latest code

This commit is contained in:
abdul-wahab12345 2024-10-08 12:30:49 +00:00
parent 203d845825
commit 98b34a2959
41 changed files with 39327 additions and 510 deletions

View File

@ -0,0 +1,46 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use \Carbon\Carbon;
use App\Models\User;
class UpdateLastOnline extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'users:update-last-online';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update last online user if he/she is not active with the period of three minutes';
/**
* Execute the console command.
*/
public function handle()
{
$now = Carbon::now()->format('Y-m-d H:i:s');
$users = User::whereNotNull('last_online')->where('last_online', '!=', '')->get();
foreach($users as $user)
{
$lastOnline = Carbon::parse($user->last_online);
if ($lastOnline->diffInMinutes($now) > 3) {
//update user
$user->is_available = 0;
$user->save();
}
}
$this->info('Users last online updated successfully.');
return 0;
}
}

View File

@ -13,6 +13,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
$schedule->command('chatgroup:check-status')->everyMinute();
$schedule->command('users:update-last-online')->everyMinute();
// $schedule->command('inspire')->hourly();
}

View File

@ -12,7 +12,9 @@
use App\Models\ChatGroup;
use App\Models\Message;
use App\Models\Tag;
use App\Models\Rule;
use App\Models\CompanyUser;
use App\Models\Notification;
function get_company_users($company_id){
@ -108,7 +110,9 @@ function insertTicket($from_email, $to_email, $subject, $content, $type, $sender
->where(function ($query) use ($subject) {
$cleanSubject = trim(str_ireplace('Re:', '', $subject)); // Remove 'Re:' prefix and trim whitespace
$query->where('subject', $cleanSubject)
->orWhere('subject', 'Re: ' . $cleanSubject); // Consider both with and without 'Re:'
->orWhere('subject', 'Re: ' . $cleanSubject)
->orWhere('subject2', 'Re: ' . $cleanSubject)
->orWhere('subject2', $cleanSubject); // Consider both with and without 'Re:'
})
->first();
@ -287,25 +291,25 @@ function getMessagesByChatId($chatId)
function setTicketMeta(int $ticketId, string $key, $value, string $type = 'string')
{
$ticket_metas = [];
// return TicketMeta::updateOrCreate(
// ['ticket_id' => $ticketId, 'key' => $key],
// ['value' => json_encode($value), 'type' => $type]
// );
return TicketMeta::updateOrCreate(
['ticket_id' => $ticketId, 'key' => $key],
['value' => json_encode($value), 'type' => $type]
);
foreach($value as $tag)
{
$ticket_meta = TicketMeta::updateOrCreate([
'ticket_id' => $ticketId,
'key' => $key
],[
'value' => $tag,
'type' => $type
]);
// foreach($value as $tag)
// {
// $ticket_meta = TicketMeta::updateOrCreate([
// 'ticket_id' => $ticketId,
// 'key' => $key
// ],[
// 'value' => $tag,
// 'type' => $type
// ]);
$ticket_metas[] = $ticket_meta;
}
// $ticket_metas[] = $ticket_meta;
// }
return $ticket_metas;
// return $ticket_metas;
}
/**
@ -321,6 +325,12 @@ function getTicketMeta(int $ticketId, string $key)
return $meta ? json_decode($meta->value) : null;
}
function getCompanyMeta(int $companyId, string $key)
{
$meta = CompanyMeta::where('company_id', $companyId)->where('key', $key)->first();
return $meta ? $meta->value : null;
}
function getChatSetting($key, $company_id = null)
{
$companyId = $company_id??getSelectedCompany();
@ -328,4 +338,41 @@ function getChatSetting($key, $company_id = null)
return $get_chat_setting;
}
function getChatSettings($key, $company_id = null)
{
$companyId = $company_id??getSelectedCompany();
$get_chat_setting = CompanyMeta::where('company_id', $companyId)->where('key', $key)->where('type', 'Chat Setting')->get();
return $get_chat_setting;
}
function getCompanyRules()
{
$companyId = getSelectedCompany();
$rules = Rule::where('company_id', $companyId)->get();
return $rules;
}
function extractEmail($fromField) {
// Regular expression to extract email address within angle brackets or standalone
if (preg_match('/<?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>?/', $fromField, $matches)) {
return $matches[1]; // Return the email address
}
return null; // Return null if no email is found
}
function send_notification($user_id,$text,$type,$status='default')
{
$notify = new Notification;
$notify->user_id = $user_id;
$notify->text = $text;
$notify->status = $status;
$notify->type = $type;
$notify->save();
return $notify;
}

View File

@ -171,6 +171,9 @@ public function checkChat(Request $request){
$domain = $request->domain;
$company = get_company('id',$company_id);
$ip = $_SERVER['REMOTE_ADDR'];
// return response()->json(['status' => 'error', 'message' => $ip]);
if($company){
// Str::contains('This is my name', 'my')
@ -180,10 +183,46 @@ public function checkChat(Request $request){
$message_when_chat_is_closed = getChatSetting('message_when_chat_is_closed',$company_id)?getChatSetting('message_when_chat_is_closed',$company_id)->value:"No user is availble right now! Try later.";
$wellcome_text = getChatSetting('wellcome_text',$company_id)?getChatSetting('wellcome_text',$company_id)->value:"Hi, welcome how i can help you today?";
//Get Style
$styles = [
'text_theme_color' => getChatSetting('text_theme_color', $company_id) ? getChatSetting('text_theme_color', $company_id)->value : null,
'background_theme_color' => getChatSetting('background_theme_color', $company_id) ? getChatSetting('background_theme_color', $company_id)->value : null,
'text_color_for_sent_message' => getChatSetting('text_color_for_sent_message', $company_id) ? getChatSetting('text_color_for_sent_message', $company_id)->value : null,
'background_color_of_sent_message' => getChatSetting('background_color_of_sent_message', $company_id) ? getChatSetting('background_color_of_sent_message', $company_id)->value : null,
'background_color_of_received_message' => getChatSetting('background_color_of_received_message', $company_id) ? getChatSetting('background_color_of_received_message', $company_id)->value : null,
'text_color_of_received_message' => getChatSetting('text_color_of_received_message', $company_id) ? getChatSetting('text_color_of_received_message', $company_id)->value : null,
'text_color_of_notification' => getChatSetting('text_color_of_notification', $company_id) ? getChatSetting('text_color_of_notification', $company_id)->value : null,
'text_color_of_error_message' => getChatSetting('text_color_of_error_message', $company_id) ? getChatSetting('text_color_of_error_message', $company_id)->value : null,
'background_color_of_error_message' => getChatSetting('background_color_of_error_message', $company_id) ? getChatSetting('background_color_of_error_message', $company_id)->value : null,
'link_color' => getChatSetting('link_color', $company_id) ? getChatSetting('link_color', $company_id)->value : null,
];
//Get Display
$settings = getChatSettings('Display Chat', $company_id);
$display_chats = $settings ? $settings->pluck('value')->map(function($value) {
return json_decode($value);
}) : null;
$hide_chats = getChatSetting('Hide Chat', $company_id) ? getChatSetting('Hide Chat', $company_id)->value : null;
$displays = [
'display_chats' => $display_chats,
'hide_chat' => $hide_chats,
];
//Get Canned Responses
$canned_responses = getChatSettings('Chat Canned Responses', $company_id);
$canned_responses = $canned_responses ? $canned_responses->pluck('value')->map(function($value) {
return json_decode($value);
}) : null;
//Terms And Conditions
$link_text = getChatSetting('link_text', $company_id) ? getChatSetting('link_text', $company_id)->value : null;
$user = $this->select_user($company_id);
if($user){
return response()->json(['status' => 'success','data' => ['welcome' => $wellcome_text, 'start_message' => $start_message, 'user' => $user->user->name] ]);
return response()->json(['status' => 'success','data' => ['welcome' => $wellcome_text, 'start_message' => $start_message, 'user' => $user->user->name, 'styles' => $styles,
'displays' => $displays, 'canned_responses' => $canned_responses, 'link_text' => $link_text] ]);
}else{
return response()->json(['status' => 'error', 'message' => $message_when_chat_is_closed]);
}

View File

@ -153,6 +153,7 @@ public function storeStyle(Request $request)
'background_theme_color' => $request->background_theme_color,
'text_color_for_sent_message' => $request->text_color_for_sent_message,
'background_color_of_sent_message' => $request->background_color_of_sent_message,
'background_color_of_received_message' => $request->background_color_of_received_message,
'text_color_of_received_message' => $request->text_color_of_received_message,
'text_color_of_notification' => $request->text_color_of_notification,
'text_color_of_error_message' => $request->text_color_of_error_message,
@ -223,7 +224,6 @@ public function storePersonalData(Request $request)
$personal_data = [
'name' => $request->name,
'link_text' => $request->link_text,
'preview' => $request->preview,
'active_approval' => $request->active_approval,
];
@ -311,4 +311,10 @@ public function blockIpAdresses(Request $request)
return redirect()->back()->with('success', 'Chat Setting Updated Successfully');
}
public function companyTermsAndConditions($companyId)
{
$link_text = getChatSetting('link_text', $companyId)->value;
return view('terms-conditions', ['link_text' => $link_text]);
}
}

View File

@ -9,6 +9,9 @@
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Models\CompanyMeta;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class DashboardController extends Controller
{
@ -17,18 +20,58 @@ public function dashboard()
$tickets = get_current_company_tickets(['type' => 'inbox']);
$companyId = getSelectedCompany();
$tags = getCompanyTags($companyId);
return view('index', ['tickets' => $tickets, 'tags' => $tags]);
$canned_response = $this->get_canned_responses();
return view('index', ['tickets' => $tickets, 'tags' => $tags, 'canned_response' => $canned_response]);
}
public function get_canned_responses(){
$companyId = getSelectedCompany();
return CompanyMeta::where('company_id', $companyId)->where('key', 'canned_responses')->get();
}
public function profile()
{
$company = getSelectedCompany();
$user = Auth::user();
$users = $users = User::where('role_id', '!=', 1)
//->where('id', '!=', Auth::id())
->join('company_users', 'users.id', '=', 'company_users.user_id')
->where('company_users.company_id', $company)
->select('users.*')
->get();
return view('profile', ['users' => $users]);
return view('profile', ['users' => $users, 'user' => $user]);
}
public function updateProfile(Request $request)
{
$request->validate([
'name' => 'required|string',
'profile_image' => 'nullable|image|mimes:jpg,jpeg,png|max:2048',
]);
$user = Auth::user();
if($request->hasFile('profile_image')) {
//Remove Old Image
if ($user->profile_image) {
$oldImagePath = str_replace('/storage/', '', $user->profile_image);
Storage::disk('public')->delete($oldImagePath);
}
//Store New Image
$file = $request->file('profile_image');
$extension = $file->getClientOriginalExtension();
$filename = time() . '_' . Str::slug(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME)) . '.' . $extension;
$path = $file->storeAs('profile_images', $filename, 'public');
$user->profile_image = Storage::url($path);
}
//update user
$user->name = $request->name;
$user->save();
return back()->with('success', 'Profile Updated Successfully');
}
}

View File

@ -30,7 +30,7 @@ public function inboxSetting()
{
$companyId = getSelectedCompany();
$timezones = Timezone::all();
$languages = Language::all();
$languages = Language::where('title', 'English (US)')->get();
$basic_setting = CompanyMeta::where('company_id', $companyId)->where('type', 'Basic Setting')->get();
$canned_response = $this->get_canned_responses();
$spam_handling = CompanyMeta::where('company_id', $companyId)->where('type', 'Spam Handling')->first();
@ -70,9 +70,9 @@ public function basicSetting(Request $request)
foreach($basic_data as $key => $value) {
CompanyMeta::updateOrCreate([
'key' => $key,
'value' => $value
],[
'company_id' => $companyId,
],[
'key' => $key,
'value' => $value,
'type' => 'Basic Setting'
@ -91,10 +91,8 @@ public function emailSignature(Request $request)
$companyId = getSelectedCompany();
CompanyMeta::updateOrCreate([
'key' => 'email_signature',
'value' => $request->email_signature
],[
'company_id' => $companyId,
'key' => 'email_signature',
],[
'value' => $request->email_signature,
'type' => 'Email Signature'
]);
@ -135,9 +133,9 @@ public function responseTime(Request $request)
if(!is_null($value)) {
CompanyMeta::updateOrCreate([
'key' => $key,
'value' => $value
],[
'company_id' => $companyId,
],[
'key' => $key,
'value' => $value,
'type' => 'Response Time'
@ -237,10 +235,10 @@ public function acknowledgementReceipt(Request $request)
if(!is_null($value)) {
CompanyMeta::updateOrCreate([
'key' => $key,
'value' => $value
],[
'company_id' => $companyId,
'key' => $key,
],[
'value' => $value,
'type' => 'Acknowledgement of Receipt'
]);
@ -362,51 +360,42 @@ public function fetchActionBox($ticketId)
public function storeResponse(Request $request)
{
$this->validate($request, [
'message' => 'required' ,
'ticket_id' => 'required' ,
'message' => 'required',
'ticket_id' => 'required',
]);
$ticket_id = $request->ticket_id;
// Handle both single and multiple ticket IDs
$ticket_ids = explode(',', $request->ticket_id);
// Load the HTML content into DOMDocument
$dom = new \DOMDocument();
libxml_use_internal_errors(true); // Prevents HTML errors from being thrown as exceptions
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $request->message);
libxml_clear_errors();
$messageWithClasses = $request->message;
// Get all <p> tags
$paragraphs = $dom->getElementsByTagName('p');
// Add classes to each <p> tag
foreach ($paragraphs as $paragraph) {
$existingClasses = $paragraph->getAttribute('class');
$paragraph->setAttribute('class', trim($existingClasses . ' user-message bg-light-green-color color-light'));
}
// Save the modified HTML
$messageWithClasses = $dom->saveHTML($dom->documentElement);
// create response
$response = createResponse($ticket_id,$messageWithClasses,1);
foreach ($ticket_ids as $ticket_id) {
$response = createResponse($ticket_id, $messageWithClasses, 1);
$ticket = Ticket::find($ticket_id);
$companyId = Session::get('selected_company');
$company = get_company('id',$companyId);
//Send mail to mailgun
if (!$ticket) continue;
$companyId = Session::get('selected_company');
$company = get_company('id', $companyId);
// Send mail to mailgun
$domain = $company->domain;
$from = $company->email;
$company_name = $company->getMeta('sender_name') ?? $company->name;
$from = "$company_name <$company->email>";
$to = $ticket->from_email;
$subject = $ticket->subject;
$html = $request->message;
// Call the function to send the email
sendEmailViaMailgun($domain, $from, $to, $subject, $html);
}
// Return the updated response and time
return response()->json([
'message' => strip_tags($response->message), // Stripping HTML tags
'created_at' => $response->created_at->format('h:i A') // Formatting time
'message' => strip_tags($response->message),
'created_at' => $response->created_at->format('h:i A')
]);
}
public function storeComment(Request $request)
@ -453,8 +442,8 @@ public function updateRule(Request $request)
'tag_id' => $request->tag_id,
'name' => $request->name,
'assign_to' => $request->assign_to,
'status' => $request->status,
'priority' => $request->priority,
'status' => isset($request->status) ? $request->status : 'set as done',
'priority' => isset($request->priority) ? $request->priority : 'Set highest priority',
'message_to_assigned_editor' => $request->message_to_assigned_editor,
'all_emails_automatically_mark_as_spam' => $request->all_emails_automatically_mark_as_spam,
]);

View File

@ -6,6 +6,9 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Models\Ticket;
use App\Models\Response;
class EmailController extends Controller
{
public function saveEmail(Request $request){
@ -14,6 +17,7 @@ public function saveEmail(Request $request){
// try {
$file_urls = [];
$token = $request->input('token');
$timestamp = $request->input('timestamp');
$signature = $request->input('signature');
@ -22,6 +26,7 @@ public function saveEmail(Request $request){
update_setting('aw_test','Invalid signature.');
abort(403, 'Invalid signature.');
}
update_setting('aw_test',json_encode($request->all()));
$data = $this->extractMailgunData($request->all());
@ -30,37 +35,61 @@ public function saveEmail(Request $request){
$to_email = $data['to_email'];
$message = $data['message'];
update_setting('aw_test',$to_email);
//update_setting('aw_test',$to_email);
$company = get_company('email',$to_email);
if($company){
$ticket = insertTicket($data['from_email'], $company->email, $data['subject'], $message,'inbox',$data['from_name'] );
if($ticket){
//Check Email if it is spam
$get_spam_handlings = getResponse($company->id,'spam_handling', 'Spam Handling');
if(!is_null($get_spam_handlings)) {
//pluck spam emails
$values = json_decode($get_spam_handlings->value, true);
$spam_emails = array_map(function ($item) {
return $item['spam_email'];
}, $values);
if(in_array($data['from_email'], $spam_emails)) {
//update status
$ticket->status = 'spam';
$ticket->save();
}
}
$this->sendEmail($company,$ticket->id);
$response = createResponse($ticket->id,$message);
$attachmentCount = $request->input('attachment-count', 0);
update_setting('aw_test',$attachmentCount);
for ($i = 1; $i <= $attachmentCount; $i++) {
$attachment = $request->file("attachment-$i");
update_setting('aw_test',$attachment->getClientOriginalName());
// update_setting('aw_test',$attachment->getClientOriginalName());
if ($attachment && $attachment->isValid()) {
// Define a unique filename, possibly using the original filename and appending a timestamp or a unique id
$filename = time() . '_' . $attachment->getClientOriginalName();
// Save the attachment to the local or specific disk
$filePath = $attachment->storeAs('tickets/' . $ticket->id, $filename, 'public');
$fileUrl = url(Storage::url($filePath));
update_setting('aw_test',$fileUrl);
$fileUrl = Storage::url($filePath);
$file_urls[] = $fileUrl;
}
}
//Update Responses Table with Attachments
if(count($file_urls) > 0) {
$response->attachments = json_encode($file_urls);
$response->save();
}
}
}else{}
// update_setting('aw_test',json_encode($request->all()));
//update_setting('aw_test',json_encode($request->all()));
// DB::commit();
// } catch (\Exception $e) {
@ -73,16 +102,41 @@ public function saveEmail(Request $request){
}
private function sendEmail($company,$ticketId)
{
$ticket = Ticket::find($ticketId);
$responses = Response::where('ticket_id', $ticket->id)->get();
$activate_delivery_confirmation = getCompanyMeta($company->id,'activate_delivery_confirmation');
$subject = getCompanyMeta($company->id,'automatic_reply_subject');
$subject = str_replace(['{title}', '{ticket}'], [$ticket->subject, $ticket->id], $subject);
$message = getCompanyMeta($company->id,'automatic_reply_text');
$message = str_replace(['{title}', '{text}', '{ticket}', '{name}'], [$ticket->subject, $ticket->content, $ticket->id, $ticket->sender_name], $message);
if ($ticket && count($responses) == 0 && !is_null($subject) && !is_null($message) && $activate_delivery_confirmation == 'on') {
$company_name = $company->getMeta('sender_name')??$company->name;
$from = "$company_name <$company->email>";
sendEmailViaMailgun($company->domain, $from, $ticket->from_email, $subject, $message);
}
//Update Ticket With Subject2
$ticket->subject2 = $subject;
$ticket->save();
}
public function extractMailgunData($data) {
// Prepare an array to hold the extracted data
$from = extractEmail($data['from']);
$to = extractEmail($data['To']);
$extractedData = [
'from_email' => $data['from'],
'to_email' => $data['To'],
'from_email' => $from,
'to_email' => $to,
'from_name' => '', // This will be extracted from the 'from' field
'subject' => $data['subject'],
'message' => $data['body-html'],
'message' => isset($data['body-html'])?$data['body-html']:$data['body-plain'],
'mime_version' => $data['Mime-Version'],
'dkim_signature' => $data['Dkim-Signature']
];

View File

@ -7,6 +7,8 @@
use App\Services\MailgunService;
use Illuminate\Support\Str;
use App\Services\CPanelApiService;
use Illuminate\Support\Facades\Storage;
class MailgunController extends Controller
{
@ -22,7 +24,16 @@ public function __construct()
public function test(){
$domain = 'test.com';
$email = "kundesone.$domain@mailgun.kundesone.no";
dd($this->createEmail($domain,$email));
$company = get_company('id',14);
// $folderPath = 'tickets/51'; // Adjust the path according to your structure
// if (Storage::disk('public')->exists($folderPath)) {
// Storage::disk('public')->deleteDirectory($folderPath);
// }
dd($company->getMeta('sender_name'));
}
public function addDomain($domain)

View File

@ -14,6 +14,7 @@
use Carbon\Carbon;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class TicketController extends Controller
{
@ -25,7 +26,7 @@ public function get_canned_responses(){
public function allTickets()
{
$companyId = getSelectedCompany();
$tickets = get_current_company_tickets();
$tickets = get_current_company_tickets(['type' => 'chat']);
$tags = Tag::where('company_id', $companyId)->get();
return view('all-tickets', ['tickets' => $tickets, 'tags' => $tags]);
}
@ -39,6 +40,7 @@ public function waiting()
public function showTicket($id)
{
$companyId = getSelectedCompany();
$tickets = get_current_company_tickets([
'type' => 'inbox',
@ -53,8 +55,15 @@ public function showTicket($id)
$messages = [];
$canned_response = $this->get_canned_responses();
$email_signature = CompanyMeta::where('company_id', $companyId)->where('type', 'Email Signature')->first();
return view('show-ticket', ['tickets' => $tickets, 'single_ticket' => $single_ticket, 'messages' => $messages, 'canned_response' => $canned_response]);
return view('show-ticket', [
'tickets' => $tickets,
'single_ticket' => $single_ticket,
'messages' => $messages,
'canned_response' => $canned_response,
'email_signature' => $email_signature?$email_signature->value:''
]);
}
public function updateStatus(Request $request, $ticketId)
@ -92,9 +101,17 @@ public function storeTags(Request $request)
$ticket_id = $request->ticket_id;
$tags = json_decode($request->tags);
setTicketMeta($ticket_id,'tags',$tags);
TicketMeta::where('key','tags')->where('ticket_id',$ticket_id)->delete();
foreach($tags as $tag)
{
TicketMeta::create(
['ticket_id' => $ticket_id, 'key' => 'tags',
'value' => $tag->value, 'type' => 'string']
);
//Update Tags Table
Tag::updateOrCreate([
'company_id' => $company,
@ -105,6 +122,8 @@ public function storeTags(Request $request)
'type' => 'inbox'
]);
//Update Company Meta Table
@ -170,6 +189,12 @@ public function deleteTickets(Request $request)
TicketMeta::where('ticket_id', $ticket_id)->delete();
Response::where('ticket_id', $ticket_id)->delete();
TicketNote::where('ticket_id', $ticket_id)->delete();
//Delete Attachments
$folderPath = "tickets/$ticket_id"; // Adjust the path according to your structure
if (Storage::disk('public')->exists($folderPath)) {
Storage::disk('public')->deleteDirectory($folderPath);
}
$ticket->delete();
}
}
@ -276,8 +301,13 @@ public function filter(Request $request)
$no_activity_tickets = $all_tickets->get();
return response()->json(['tickets' => $no_activity_tickets]);
} elseif($request->filter == 'Spam') {
$all_tickets = $tickets->where('status', $request->status)->get();
if($request->status == 'marked as spam') {
$all_tickets = $tickets->where('status', 'spam')->get();
return response()->json(['tickets' => $all_tickets]);
} else {
$all_tickets = $tickets->where('status', '!=', 'spam')->get();
return response()->json(['tickets' => $all_tickets]);
}
} elseif($request->filter == 'Status') {
$all_tickets = $tickets->where('status', $request->status)->get();
return response()->json(['tickets' => $all_tickets]);

View File

@ -10,4 +10,9 @@ class Company extends Model
use HasFactory;
protected $guarded = [];
public function getMeta($key){
return getCompanyMeta($this->id,$key);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Notification extends Model
{
use HasFactory;
protected $guarded = [];
}

View File

@ -22,6 +22,7 @@ class User extends Authenticatable
'email',
'password',
'role_id',
'profile_image'
];
/**

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('tickets', function (Blueprint $table) {
$table->string('subject2')->nullable()->after('subject');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('tickets', function (Blueprint $table) {
$table->dropColumn('subject2');
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('responses', function (Blueprint $table) {
$table->longText('attachments')->nullable()->after('message');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('responses', function (Blueprint $table) {
$table->dropColumn('attachments');
});
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('notifications', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
$table->text('text')->nullable();
$table->string('status')->nullable();
$table->string('type')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('notifications');
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('profile_image')->nullable()->after('remember_token');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('profile_image');
});
}
};

37617
error_log

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ function initFomanticDropdown(selector, config, callback = null) {
}
function slideTransition(selector, direction, shouldHide) {
$(selector).transition(`slide ${direction}`);
//$(selector).transition(`slide ${direction}`);
if (shouldHide) {
$(selector).addClass("hidden");
} else {

BIN
public/dummy-image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
public/dummy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -190,12 +190,7 @@ function updateStatusOptions(selectedFilter) {
@foreach($company_users as $company_user)
options += '<option value="{{ $company_user->user->id }}">{{ $company_user->user->name }}</option>';
@endforeach
// $('#status-select').html(`
// <option disabled value="">Select Users</option>`
// @foreah($company_users as $company_user)
// `<option value="`{{$company_user->user->id}}`">`{{$company_user->user->name}}`</option>
// <option value="Abdullah">Abdullah</option>
// `);
// Update the select element with the generated options
$('#status-select').html(options);
$('.filter_based__data').show();
@ -343,6 +338,86 @@ function updateStatusOptions(selectedFilter) {
/* Show checkboxes when 'Handle Multiple' is active */
.handle-multiple-active .checkbox-wrapper {
display: block;
}
.chat-content {
padding: 12px 11px !important;
}
.chat-user-img{
margin-left:12px !important;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
/* When checkbox is checked, set background to green */
input[type="checkbox"]:checked {
background-color: #748C62;
border-color: #748C62;
}
/* Optional: Add checkmark icon or any visual effect */
input[type="checkbox"]:checked::before {
transform: translate(0px, -1px);
content: '✔';
display: block;
text-align: center;
color: white;
font-weight: bold;
font-size: 13px;
margin-bottom: -1px;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
margin-right: 7px;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
/* margin-top: 12px; */
transform: translate(2px, 6px);
}
#cannedResponseModal ul {
list-style: none;
padding: 0;
display: flex !important;
gap: 8px !important;
flex-wrap: wrap !important;
}
.canned-response {
background-color: #748C62 !important;
color: white;
}
</style>
<div class="content-wrapper">
@ -410,7 +485,7 @@ function updateStatusOptions(selectedFilter) {
<input type="checkbox" class="ticket-checkbox" id="ticket-{{$ticket->id}}">
</div>
<div class="chat-user-img all-tickets position-relative">
<img src="{{ asset('images/Avatar.png') }}" alt="User">
<img style="height: 42px; width: 42px; border-radius: 50%;" src="{{ asset('dummy-image.jpg') }}" alt="User">
<div
class="chat-status-icon rounded-circle text-center align-content-center position-absolute">
<img src="{{ asset('images/icons/chat-round.svg') }}" alt="Chat Round">

View File

@ -623,6 +623,17 @@ class="form-control input-reply-textarea message_when_chat_is_closed">{!! $messa
<span style="background-color: {{ $background_color_of_sent_message_value }};"></span>
</div>
</div>
<div class="dev-box">
<h3>Background color of received messages</h3>
@php
$background_color_of_received_message = getChatSetting('background_color_of_received_message');
$background_color_of_received_message_value = $background_color_of_received_message->value ?? '#020400';
@endphp
<div class="dev-box-inner">
<input type="color" name="background_color_of_received_message" readonly placeholder="#020400" value="{{$background_color_of_received_message_value}}">
<span style="background-color: {{ $background_color_of_received_message_value }};"></span>
</div>
</div>
<div class="dev-box">
<h3>Text color of received messages</h3>
@php
@ -785,7 +796,7 @@ class="form-control input-reply-textarea message_when_chat_is_closed">{!! $messa
<p>Select if users must click on a checkbox to approve policy before they can
contact
you.</p>
<form method="POST" action="{{ route('store.personal.data') }}">
<form style="max-width: none;" method="POST" action="{{ route('store.personal.data') }}">
@csrf
<div class="dev-input-group">
<label class="dev-checkbox-wrapper">Require active approval (check box)
@ -806,28 +817,34 @@ class="form-control input-reply-textarea message_when_chat_is_closed">{!! $messa
<div class="dev-input-group dev-input-group-input-info">
<label>Link text</label>
@php
$link_text = getChatSetting('link_text')
$link_text = '';
if(getChatSetting('link_text')){
$link_text = getChatSetting('link_text')->value;
}
@endphp
<input type="text" placeholder="Type here" name="link_text" required value="{{ $link_text->value ?? '' }}">
<textarea name="link_text" id="text_editor" rows="10" placeholder="Your Message"
class="form-control input-reply-textarea" required>{!! $link_text ?? '' !!}</textarea>
</div>
<div class="dev-input-group dev-input-group-input-info">
<label>Preview</label>
@php
$preview = getChatSetting('preview')
@endphp
<input type="text" name="preview" value="{{ $preview->value ?? '' }}">
</div>
<div class="dev-input-group dev-input-group-input-info dev-custom-input-group">
<label>Policy for personal data</label>
<p>Custom policy</p>
<button type="button" class="dev-form-submit-btn">Edit</button>
<a style="text-decoration: none;" href="{{ route('company.terms.conditions', getSelectedCompany()) }}" target="_blank">Terms & Conditions</a>
</div>
<!--<div class="dev-input-group dev-input-group-input-info dev-custom-input-group">-->
<!-- <label>Policy for personal data</label>-->
<!-- <p>Custom policy</p>-->
<!-- <button type="button" class="dev-form-submit-btn">Edit</button>-->
<!--</div>-->
<button type="submit" class="dev-form-submit-btn">Save</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.ckeditor.com/4.16.0/standard/ckeditor.js"></script>
<script>
CKEDITOR.replace('text_editor');
</script>
<!-- -->
<!--<div class="dev-tabcontent dev-tabcontent-tags">-->
<!-- <div class="dev-tabcontent-outers">-->

View File

@ -7,7 +7,17 @@
.scrollhint .item .single-user-content img,.scrollhint .item .sender-message-box img{
width:100%;
}
@media screen and (max-width: 767px) {
.chat-message-box {
left: 20px !important;
}
}
@media only screen and (max-width: 525px) {
div.chat-inbox>.chat-content-wrapper>.chat-message>.single-message-chat>.user-message {
max-width: 100% !important;
width: 100% !important;;
}
}
</style>
<!-- Support and Chat Fixed Buttons -->
@ -79,7 +89,7 @@
<!-- Support Drop down -->
<div class="ui floating bg-dark-green-color support-widget-wrapper icon dropdown button ">
<img src="images/icons/support.svg" alt="Support Icon">
<img src="{{asset('images/icons/support.svg')}}" alt="Support Icon">
<div class="menu support-widget">
<div class="header support-header mt-0 text-center">
<h2 class="color-dark-green">Help & Support</h2>
@ -87,49 +97,49 @@
<div class="support-facilities-box d-flex justify-content-between flex-wrap">
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/faq-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/faq-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green">FAQ</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/laptop-minimalistic-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/laptop-minimalistic-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green">Using Kundo</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/launch-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/launch-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green text-wrap">Launching Kundo</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/setting-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/setting-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green text-wrap">Technical Settings</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/data-mapping-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/data-mapping-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green ">Integration</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/open-door-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/open-door-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green text-wrap">Privacy & Policy</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/news-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/news-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green text-wrap">News & Updates</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/graduate-cap-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/graduate-cap-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green ">Training</p>
</div>
@ -142,13 +152,13 @@
<div class="contact-us-box d-flex justify-content-center">
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/email-14-svgrepo-com (1) 1.svg" alt="">
<img src="{{asset('images/icons/email-14-svgrepo-com (1) 1.svg')}}" alt="">
</div>
<p class="color-dark-green text-wrap">Technical Questions</p>
</div>
<div class="item text-center">
<div class="support-img-box align-content-center">
<img src="images/icons/about-filled-svgrepo-com 1.svg" alt="">
<img src="{{asset('images/icons/about-filled-svgrepo-com 1.svg')}}" alt="">
</div>
<p class="color-dark-green">About Kundo</p>
</div>
@ -570,3 +580,50 @@ function playMessageSound() {
</script>
<script>document.querySelector('.input-action img[alt="Attachment"]').addEventListener('click', function() {
document.getElementById('file-picker').click();
});
</script>
<style>
.outer-message-input-box {
width: 100%;
padding: 10px;
background-color: #f8f9fa;
}
.inner-message-input {
width: 100%;
background-color: #ffffff;
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.inner-message-input input {
flex-grow: 1;
margin-right: 10px;
}
.input-action img {
margin-left: 10px;
cursor: pointer;
}
.input-action .file-picker {
display: none;
}
.fa-paper-plane-o {
font-size: 17px !important;
background: #748C62 !important;
padding: 3px 7px !important;
color: white !important;
border-radius: 3px !important;
cursor: pointer !important;
}
</style>

View File

@ -1,3 +1,93 @@
<style>
#cke_editor1{
width:100%!important;
}
div.chat-inbox.chat-box{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 100%;
max-width: 600px;
}
.close-button {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
background:;#748c62 !important
}
.close-button:hover,
.close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
#cannedResponseModal ul {
list-style: none;
padding: 0;
}
#cannedResponseModal li {
padding: 8px 0; /* Spacing between buttons */
}
.canned-response {
width: 100%;
padding: 10px;
font-size: 16px;
border: none;
background-color: #4CAF50;
color: white;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s, box-shadow 0.3s;
}
.canned-response:hover {
background-color: #45a049;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.24);
}
.close-button {
color: #aaa;
/* float: right; */
font-size: 28px;
font-weight: bold;
background: ;
display: flex !important;
justify-content: flex-end !important;
margin-top: -14px;
font-size: 32px;
}
</style>
<!-- Custom Modal post -->
<div id="customModal" class="modal">
<div class="modal-content">
@ -45,10 +135,15 @@
<div id="customModal3" class="modal">
<div class="modal-content">
<span class="modal-close">&times;</span>
<h5>Replay to multiple</h5>
<h5>Reply to multiple</h5>
<form>
<div class="mb-3 mt-4">
<p>Please choose only email conversations and try again</p>
<div class="content d-flex align-items-end flex-column message-writing-content-area">
<textarea name="email_signature" id="editor1" rows="10" placeholder="Your Message"
class="form-control input-reply-textarea" required></textarea>
</div>
<div style="display: flex;flex-direction: row-reverse;">
<button type="button" class="btn btn-primary reply-to-multiple">Reply</button>
</div>
</form>
@ -56,6 +151,135 @@
</div>
</div>
<!-- Canned Response Modal -->
<div id="cannedResponseModal" class="modal">
<div class="modal-content">
<span class="close-button">&times;</span>
<h2>Canned Responses</h2>
<ul>
@php
$companyId = getSelectedCompany();
$canned_response = \App\Models\CompanyMeta::where('company_id', $companyId)->where('key', 'canned_responses')->get();
$email_signature = \App\Models\CompanyMeta::where('company_id', $companyId)->where('type', 'Email Signature')->first()?->value;
@endphp
@if(count($canned_response) > 0)
@foreach($canned_response as $index => $value)
@php
$result = json_decode($value->value);
@endphp
<li><button class="canned-response" data-response="{{$result->text}}">{{$result->name}}</button></li>
@endforeach
@endif
</ul>
</div>
</div>
<script>
//CKEDITOR.replace('editor1');
CKEDITOR.plugins.add('addcannedresponse', {
init: function(editor) {
// Command for inserting canned response
editor.addCommand('addCannedResponseCmd', {
exec: function(editor) {
// Show your modal or handle canned response insertion
document.getElementById('cannedResponseModal').style.display = 'block';
}
});
// Command for inserting signature
editor.addCommand('addSignatureCmd', {
exec: function(editor) {
var signatureHtml = `<br>{!! $email_signature !!}`; // Signature content
CKEDITOR.instances.editor1.insertHtml(signatureHtml);
}
});
// Add "Insert Canned Response" button
editor.ui.addButton('AddCannedResponse', {
label: 'Insert Canned Response',
command: 'addCannedResponseCmd',
icon: 'https://kundesone.no/images/canned.png', // Use an accessible icon URL or local path
toolbar: 'insert,0'
});
// Add "Insert Signature" button
editor.ui.addButton('AddSignature', {
label: 'Insert Signature',
command: 'addSignatureCmd',
icon: 'https://kundesone.no/images/signature-icon.png', // Placeholder icon URL, replace with a valid one
toolbar: 'insert,1'
});
}
});
CKEDITOR.replace('editor1', {
extraPlugins: 'addcannedresponse', // Ensure your plugin is added to extraPlugins
// Optionally customize your toolbar further, or use the default configuration
toolbarGroups: [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
{ name: 'forms' },
{ name: 'tools' },
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
{ name: 'others' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' },
{ name: 'about' }
]
});
// Get the modal
var modal = document.getElementById("cannedResponseModal");
// Get the button that opens the modal
var btn = document.getElementsByClassName("canned-response");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close-button")[0];
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
// Add event listeners to canned response buttons
Array.from(btn).forEach(function(element) {
element.addEventListener('click', function() {
var response = this.getAttribute('data-response');
CKEDITOR.instances.editor1.insertHtml(response);
modal.style.display = "none";
});
});
</script>
<script>
$(document).ready(function() {
var $handleMultipleButton = $('.handle-multiple-btn');
@ -278,3 +502,69 @@ function updateButtonStates() {
</script>
<!--Update Status End-->
<!-- Reply to Multiple Start -->
<script>
$(document).ready(function() {
$('.reply-to-multiple').on('click', function(){
var message = CKEDITOR.instances.editor1.getData();
if(message.trim() === '') {
toastr.error('Message cannot be empty');
return;
}
var selectedTickets = [];
$('.ticket-checkbox:checked').each(function() {
selectedTickets.push($(this).attr('id').replace('ticket-', ''));
});
var ticket_ids = selectedTickets.join(',');
console.log(ticket_ids);
// SweetAlert2 confirmation dialog
Swal.fire({
title: 'Are you sure?',
text: 'are you sure to send reply?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, send it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: '/store/response',
method: 'POST',
data: {
ticket_id: ticket_ids,
message: message,
_token: '{{ csrf_token() }}'
},
success: function(response) {
// Show success notification
Swal.fire(
'Updated!',
'Reply has been sent successfully.',
'success'
);
// Optionally reload or update the page
location.reload();
},
error: function(xhr) {
// Show error notification
Swal.fire(
'Error!',
'An error occurred. Please try again.',
'error'
);
}
});
}
});
});
});
</script>
<!-- Reply to Multiple End -->

View File

@ -61,13 +61,95 @@
.slider.round:before {
border-radius: 50%;
}
.chat{
transform: translate(15px, 1px);
font-size: 17px;
font-weight: 500;
}
.in_setting{
display: none !important;
}
@media screen and (max-width: 767px) {
div.inbox-content-wrapper>.inbox-inner-wrapper>.user-box {
width: 90px;
display: none !important;
}
}
@media only screen and (max-width: 420px) {
.logout {
display: none !important;
}
.in_setting{
display: block !important;
margin-left:12px;
}
@media only screen and (max-width: 348px) {
header .nav-links {
gap: 17px;
}
.chat {
transform: translate(7px, 1px) !important;
font-size: 17px;
font-weight: 500;
}
}
@media only screen and (max-width: 525px) {
div.chat-inbox>.chat-content-wrapper>.chat-message>.single-message-chat>.user-message {
max-width: 100% !important;
width: 100% !important;;
}
}
@media only screen and (max-width: 315px) {
header .nav-links {
gap: 15px;
}
.chat {
transform: translate(7px, 1px) !important;
font-size: 17px;
font-weight: 500;
}
}
.logout_form {
display: flex;
justify-content: center;
margin-top: 14px;
margin-bottom: 10px;
}
.logout_form .nav-btn {
border: none;
outline: none;
border-radius: 6.26px;
height: 38px;
width: 126.89px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
@media screen and (max-width: 767px) {
.content-area header .row .col-sm-8 {
flex-direction: column;
align-items: center !important;
gap: 10px;
}
}
.single-message-chat {
width: 100%;
height: -webkit-fill-available !important;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<header>
<div class="row">
<div class="col-sm-4 user-name-nav-area d-flex align-content-center">
<div class="dev-toggle-sidebar">
<img src="{{ asset('images/icons/blocks-icon.svg') }}">
<img src="https://kundesone.no/images/logo_cropped.png">
</div>
<h2 class="d-flex align-items-center">Hello {{auth()->user()->name}} <span>👋🏼</span>,</h2>
</div>
@ -77,11 +159,12 @@
<input type="text" class="color-dark-green" placeholder="Search...">
</div>
<div class="nav-links d-flex align-items-center">
<span class="chat">Chat </span>
<label class="switch">
<input type="checkbox" id="toggleSwitch" @if(auth()->user()->is_available == 1) checked @endif>
<span class="slider round"></span>
<span class="slider round">
</label>
<form method="POST" action="{{route('logout')}}">
<form class="logout" method="POST" action="{{route('logout')}}">
@csrf
<button class="nav-btn bg-dark-green-color">
<img height="25" width="50" src="{{ asset('images/logout.png') }}" alt="">
@ -146,6 +229,7 @@ class="chat-settings-btn-row text-center d-flex justify-content-center align-ite
<a
class="ui secondary basic button shadow-none setting-btn text-white align-content-center">Settings</a>
</div>
</div>
<div class="item">
<p class="action-heading-paragraph text-center align-content-center">
@ -158,6 +242,14 @@ class="chat-settings-btn-row text-center d-flex justify-content-center align-ite
<!--<a class="ui secondary basic button tag-btn shadow-none">Tags</a>-->
<a href="{{ route('profile') }}"
class="ui secondary basic button shadow-none setting-btn text-white align-content-center">Settings</a>
</div> <hr >
<div class="logout_form">
<form class="in_setting" method="POST" action="https://kundesone.no/logout">
<input type="hidden" name="_token" value="18hlNz76CXQJdP55j8LVsnhIf8KpaEi5MXKu9EFV" autocomplete="off"> <button class="nav-btn bg-dark-green-color">
<img height="25" width="50" src="https://kundesone.no/images/logout.png" alt="">
</button>
</form>
</div>
</div>
</div>
@ -189,11 +281,16 @@ class="ui secondary basic button shadow-none setting-btn text-white align-conten
<!--chat avialability ajax-->
<script>
$(document).ready(function() {
$('#toggleSwitch').on('change', function() {
const isChecked = $(this).is(':checked');
$('#toggleSwitch').off('change').on('change', function(e) {
// Prevent default form submission if the toggle is inside a form
e.preventDefault();
const isChecked = $(this).is(':checked');
let status = isChecked ? 'on' : 'off';
// Disable the checkbox to avoid multiple clicks during the AJAX call
$(this).prop('disabled', true);
if (isChecked) {
// Call route when toggle is ON
$.ajax({
url: 'update/chat-availability',
method: 'POST',
@ -201,41 +298,35 @@ class="ui secondary basic button shadow-none setting-btn text-white align-conten
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
data: {
status: 'on'
status: status
},
success: function(response) {
console.log('Success:', response);
if(response.success) {
toastr.success('Chat Availability Updated Successfully');
}
// Enable the checkbox again after the AJAX call completes
$('#toggleSwitch').prop('disabled', false);
},
error: function(xhr, status, error) {
console.error('Error:', error);
$('#toggleSwitch').prop('disabled', false);
}
});
} else {
// Call route when toggle is OFF
$.ajax({
url: 'update/chat-availability',
method: 'POST',
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
data: {
status: 'off'
},
success: function(response) {
console.log('Success:', response);
if(response.success) {
toastr.success('Chat Availability Updated Successfully');
}
},
error: function(xhr, status, error) {
console.error('Error:', error);
}
});
}
});
});
</script>
<style>
@media screen and (max-width: 767px) {
.content-area header .row .col-sm-8 {
flex-direction: column;
align-items: center !important;
gap: 10px;
}
}
</style>

View File

@ -1,8 +1,8 @@
<div class="sidebar-area">
<aside class="bg-dark-green-color">
<div class="image-box d-flex justify-content-between">
<div class="image-box d-flex justify-content-between frame">
<img src="{{ asset('images/logo-white.png') }}" alt="Site Logo">
<div class="dev-toggle-sidebar dev-close"><img src="{{ asset('images/icons/close-icon.svg') }}" alt=""></div>
<div class="dev-toggle-sidebar dev-close"><img src="https://kundesone.no/images/icons/Frame.png" alt=""></div>
</div>
<style>
@ -15,6 +15,12 @@
}
.bg-light-color .sidebar_icon{
color: #383F33 !important;
}
.frame .dev-toggle-sidebar img {
width: 26px;
height: 26px;
vertical-align: top !important;
border-radius: 50%;
}
</style>
@ -49,7 +55,7 @@ class="side-bar-link d-flex align-items-center justify-content-between aw-a-wai
class="side-bar-link d-flex align-items-center justify-content-between aw-a-all">
<div class="link-left-content align-items-center d-flex">
<i class="fa fa-ticket sidebar_icon" aria-hidden="true"></i>
<h6 class="color-light">All Tickets</h6>
<h6 class="color-light">Chats</h6>
</div>
<div class="link-right-content">
<img src="{{ asset('images/icons/chevron-right-light.png') }}" alt="chevron right">
@ -114,12 +120,15 @@ class="dropdown-item-label color-light">FORUM</span></p>
<div class="profile-nav-row d-flex side-bar-link">
<div class="profile-nav-box">
<div class="img-box"><img src="{{ asset('images/user.png') }}" alt="User Image"></div>
@if(!is_null(Auth::user()->profile_image))
<div class="img-box"><a href="{{route('profile')}}"><img style="height: 42px; width: 42px; border-radius: 50%" src="{{ url('' . Auth::user()->profile_image) }}" alt="User Image"></a></div>
@else
<div class="img-box"><img style="height: 42px; width: 42px; border-radius: 50%" src="{{ asset('dummy-image.jpg') }}" alt="User Image"></div>
@endif
</div>
<div class="user-info-box d-flex justify-content-between">
<div class="info">
<p class="color-light side-bar-user-name">Maxwell</p>
<p class="color-offset-white">Project Manager</p>
<a style="text-decoration: none; color: white;" href="{{route('profile')}}"><p class="color-light side-bar-user-name">{{Auth::user()->name}}</p></a>
</div>
<div class="icon align-self-center">
<img src="{{ asset('images/icons/chevron-down 3.png') }}" alt="Chevron Down Icon">

View File

@ -10,56 +10,133 @@
<meta content="" name="description">
<meta content="" name="keywords">
<!-- Favicons -->
<link href="" rel="icon">
<link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>
<!-- Bootstrap Styles -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link href="https://api.fontshare.com/v2/css?f[]=satoshi@300,301,400,401,500,501,700,701,900,901&display=swap"
rel="stylesheet">
<!-- font-awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<link rel="stylesheet" href="{{ asset('assets/auth.css') }}">
<!-- Toastr CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
<style>
body {
font-family: 'Satoshi', sans-serif;
background-color: #f4f7f9;
color: #333;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 90%;
margin: 40px auto;
padding: 30px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
h1, h2 {
margin-bottom: 15px;
color: #4A4A4A;
}
.alert {
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
font-weight: bold;
}
.domain-details, .dns-records {
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.record {
background-color: #ffffff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
margin-bottom: 10px;
transition: background-color 0.3s, transform 0.2s;
}
.record:hover {
background-color: #f1f1f1;
transform: translateY(-1px);
}
.btn.signup {
background: #748C62;
color: white;
border: none;
padding: 12px 25px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s, transform 0.2s;
}
.btn.signup:hover {
background: #45a049;
transform: translateY(-1px);
}
@media (max-width: 768px) {
.container {
padding: 20px;
}
.btn.signup {
width: 100%;
font-size: 18px;
}
}
p:last-child {
margin-bottom: 0;
overflow-wrap: break-word;
}
.verify{
display: flex;
justify-content: flex-end;
}
</style>
</head>
<body>
<div class="container">
<h1>Instructions to verify domain. Read and apply carefully.</h1>
<h1>Instructions to Verify Domain</h1>
<div class="domain-details">
<div class="alert alert-{{$domain->getDomain()->getState() == 'active'?'success':'danger'}}">
<ul>
<li>Domain is {{$domain->getDomain()->getState()}}</li>
</ul>
</div>
<h2>Domain Information</h2>
<p><strong>Name:</strong> {{ $domain->getDomain()->getName() }}</p>
<p><strong>State:</strong> {{ $domain->getDomain()->getState() }}</p>
</div>
<div class="dns-records">
<h2>Step 1: Add Forwarder</h2>
<div class="record">
<p>Forward your email to internal email. i.e <span class="alert-success"><b>kundesone.{{ $domain->getDomain()->getName() }}@mailgun.kundesone.no</b></span>. Make sure you forward only your company email that you added on the time of registration.</p>
<p>Forward your email to internal email: <span class="alert-success"><b>kundesone.{{ $domain->getDomain()->getName() }}@mailgun.kundesone.no</b></span>. Make sure to forward only your company email that you registered.</p>
</div>
<h2>Step 2: Add Outbound DNS Records</h2>
@foreach($domain->getOutboundDnsRecords() as $record)
<div class="record">
@ -69,7 +146,6 @@
</div>
@endforeach
<h2>Step 3: Add Inbound DNS Records</h2>
@foreach($domain->getInboundDnsRecords() as $record)
<div class="record">
@ -80,80 +156,20 @@
</div>
@endforeach
<h2>Note: DNS Propagation Time</h2>
<div class="record">
<p>DNS Propagation can take upto 48 hours. In this case your domain will not be active.</p>
<p>DNS propagation can take up to 48 hours. During this time, your domain may not be active.</p>
</div>
</div>
<form id="verify-form" action="{{route('verifyDomain')}}" method="post">
@csrf
<input type="hidden" name="domain" value="{{$domain->getDomain()->getName()}}"/>
<button type="submit" class="btn signup">Verify Domain
</button>
<div class="verify">
<button type="submit" class="btn signup">Verify Domain</button>
</div>
</form>
</div>
<style>
#verify-form{
display:flex;
justify-content: flex-end;
}
.btn.signup{
background:#748c62;
color:white;
}
.container {
width: 80%;
margin: 0 auto;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1, h2 {
color: #333;
}
.message {
background-color: #f0f0f0;
padding: 10px;
border-left: 5px solid green;
margin-bottom: 20px;
}
.domain-details, .dns-records {
background-color: #fff;
border: 1px solid #ddd;
padding: 10px;
margin-bottom: 20px;
}
.record {
background-color: #f9f9f9;
border: 1px solid #eee;
padding: 8px;
margin-bottom: 10px;
word-break: break-all;
}
</style>
</body>
</html>

View File

@ -14,7 +14,7 @@
<button type="button">Response</button>
<button type="button">Canned Responses</button>
<button type="button">Acknowledge</button>
<button type="button">Rules</button>
<!--<button type="button">Rules</button>-->
<button type="button">Spam handling</button>
<!--<button type="button">Tags</button>-->
<!--<button type="button">Others</button>-->
@ -46,7 +46,7 @@
</div>
<div class="dev-input-group dev-input-group-input-info">
<label>Inbox name</label>
<input name="inbox_name" type="text" placeholder="Type here.." value="{{ $basic_setting[1]['value'] ?? '' }}" required>
<input name="inbox_name" type="text" placeholder="Type here.." value="{{ $company->getMeta('inbox_name') ?? '' }}" required>
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>Your internal name. I.e. the Inbox</span>
@ -54,7 +54,7 @@
</div>
<div class="dev-input-group dev-input-group-input-info">
<label>Sender name</label>
<input name="sender_name" type="text" placeholder="Type here.." value="{{ $basic_setting[2]['value'] ?? '' }}" required>
<input name="sender_name" type="text" placeholder="Type here.." value="{{ $company->getMeta('sender_name') ?? '' }}" required>
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>E.g. company name or department</span>
@ -69,7 +69,7 @@
<select name="language" required>
<option value="">Select Language</option>
@foreach($languages as $language)
<option value="{{$language->value}}" @if(count($basic_setting) > 0 && $basic_setting[3]['value'] == $language->value) selected @endif>{{$language->title}}</option>
<option value="{{$language->value}}" @if(count($basic_setting) > 0 && $company->getMeta('language') == $language->value) selected @endif>{{$language->title}}</option>
@endforeach
</select>
</div>
@ -80,7 +80,7 @@
<select name="timezone" required>
<option value="">Select your Timezone</option>
@foreach($timezones as $timezone)
<option value="{{$timezone->label}}" @if(count($basic_setting) > 0 && $basic_setting[4]['value'] == $timezone->label) selected @endif>{{$timezone->label}}</option>
<option value="{{$timezone->label}}" @if(count($basic_setting) > 0 && $company->getMeta('timezone') == $timezone->label) selected @endif>{{$timezone->label}}</option>
@endforeach
</select>
</div>
@ -658,98 +658,98 @@ class="form-control input-reply-textarea">{!! $automatic_reply_text->value ?? ''
</div>
<!-- -->
<div class="dev-tabcontent dev-tabcontent-rules">
<div class="dev-tabcontent-outers">
<div class="dev-title-row">
<h2>Automatic rules</h2>
<p>
With automatic rules you can perform common tasks automatically, e.g. assign an
e-mail to a specific person if the subject contains a certain word. Automatic
rules consist of a filter and an effect.
</p>
</div>
<h2>Create a new rule</h2>
<form method="POST" action="{{ route('update.rule') }}">
@csrf
<div class="dev-input-group dev-input-group-input-info">
<label>Name</label>
<input name="name" type="text" placeholder="Type here.." value="{{$rule->name ?? ''}}">
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>Tag everything from the sales department</span>
</div>
</div>
<h3>Filter</h3>
<p>If you complete the From, To, and Subject fields, they must all match an email for
the rule to be activated. Leave any field empty to avoid having to match that part.
If you specify multiple email addresses in the same field, it's enough for one of
them to match.
If, for example, you fill in <a href="#">support@kundo.se</a> and <a
href="#">info@kundo.se</a> in the From field,
and "hello" in the subject field, all e-mails from <a href="#">support@kundo.se</a>
OR <a href="#">info@kundo.se</a>
that ALSO contains "hello" in the subject field will match.
If you want to activate a rule for all emails that come from a group of email
addresses, you can write *@kundo.se in the "from" field.
</p>
<div class="dev-form-inner">
<div class="col-left">
<div class="dev-input-group dev-input-group-input-info">
<label>From</label>
<input type="email" name="from" placeholder="Type here.." value="{{$rule->from ?? ''}}">
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>E.g. example@example.com or *@example.com</span>
</div>
</div>
<div class="dev-input-group dev-input-group-input-info">
<label>To</label>
<input type="email" name="to" placeholder="Type here.." value="{{$rule->to ?? ''}}">
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>E.g. test@example.com</span>
</div>
</div>
</div>
<div class="col-right">
<div class="dev-input-group dev-input-group-input-info">
<label>Subject Contains</label>
<input type="text" name="subject_contains" placeholder="Type here.." value="{{$rule->subject_contains ?? ''}}">
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>E.g. Great deals!</span>
</div>
</div>
<div class="dev-input-group dev-input-group-input-info">
<label>Text Contains</label>
<input type="text" name="text_contains" placeholder="Type here.." value="{{$rule->text_contains ?? ''}}">
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>E.g. Great deals!</span>
</div>
</div>
</div>
</div>
<div class="dev-input-group">
<label class="dev-checkbox-wrapper">All e-mails automatically marked as spam
<input name="all_emails_automatically_mark_as_spam" type="checkbox" @if($rule && !is_null($rule->all_emails_automatically_mark_as_spam)) checked @endif>
<span class="checkmark"></span>
</label>
</div>
<h3>Exceptions</h3>
<p>Email that matches the filter above but for which the rule should not be activated.
</p>
<div class="dev-form-inner">
<div class="col-left">
<div class="dev-input-group dev-input-group-input-info">
<label>Subject Contains</label>
<textarea rows="6" name="subject1_contains">{{$rule->subject1_contains ?? ''}}</textarea>
<div class="dev-input-info">
<img src="{{ asset('images/info.svg') }}" alt="info">
<span>Order Confirmation</span>
</div>
</div>
</div>
<!--<div class="dev-tabcontent dev-tabcontent-rules">-->
<!-- <div class="dev-tabcontent-outers">-->
<!-- <div class="dev-title-row">-->
<!-- <h2>Automatic rules</h2>-->
<!-- <p>-->
<!-- With automatic rules you can perform common tasks automatically, e.g. assign an-->
<!-- e-mail to a specific person if the subject contains a certain word. Automatic-->
<!-- rules consist of a filter and an effect.-->
<!-- </p>-->
<!-- </div>-->
<!-- <h2>Create a new rule</h2>-->
<!-- <form method="POST" action="{{ route('update.rule') }}">-->
<!-- @csrf-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Name</label>-->
<!-- <input name="name" type="text" placeholder="Type here.." value="{{$rule->name ?? ''}}">-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>Tag everything from the sales department</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <h3>Filter</h3>-->
<!-- <p>If you complete the From, To, and Subject fields, they must all match an email for-->
<!-- the rule to be activated. Leave any field empty to avoid having to match that part.-->
<!-- If you specify multiple email addresses in the same field, it's enough for one of-->
<!-- them to match.-->
<!-- If, for example, you fill in <a href="#">support@kundo.se</a> and <a-->
<!-- href="#">info@kundo.se</a> in the From field,-->
<!-- and "hello" in the subject field, all e-mails from <a href="#">support@kundo.se</a>-->
<!-- OR <a href="#">info@kundo.se</a>-->
<!-- that ALSO contains "hello" in the subject field will match.-->
<!-- If you want to activate a rule for all emails that come from a group of email-->
<!-- addresses, you can write *@kundo.se in the "from" field.-->
<!-- </p>-->
<!-- <div class="dev-form-inner">-->
<!-- <div class="col-left">-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>From</label>-->
<!-- <input type="email" name="from" placeholder="Type here.." value="{{$rule->from ?? ''}}">-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>E.g. example@example.com or *@example.com</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>To</label>-->
<!-- <input type="email" name="to" placeholder="Type here.." value="{{$rule->to ?? ''}}">-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>E.g. test@example.com</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-right">-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Subject Contains</label>-->
<!-- <input type="text" name="subject_contains" placeholder="Type here.." value="{{$rule->subject_contains ?? ''}}">-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>E.g. Great deals!</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Text Contains</label>-->
<!-- <input type="text" name="text_contains" placeholder="Type here.." value="{{$rule->text_contains ?? ''}}">-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>E.g. Great deals!</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="dev-input-group">-->
<!-- <label class="dev-checkbox-wrapper">All e-mails automatically marked as spam-->
<!-- <input name="all_emails_automatically_mark_as_spam" type="checkbox" @if($rule && !is_null($rule->all_emails_automatically_mark_as_spam)) checked @endif>-->
<!-- <span class="checkmark"></span>-->
<!-- </label>-->
<!-- </div>-->
<!-- <h3>Exceptions</h3>-->
<!-- <p>Email that matches the filter above but for which the rule should not be activated.-->
<!-- </p>-->
<!-- <div class="dev-form-inner">-->
<!-- <div class="col-left">-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Subject Contains</label>-->
<!-- <textarea rows="6" name="subject1_contains">{{$rule->subject1_contains ?? ''}}</textarea>-->
<!-- <div class="dev-input-info">-->
<!-- <img src="{{ asset('images/info.svg') }}" alt="info">-->
<!-- <span>Order Confirmation</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!--<div class="col-right">-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Text Contains</label>-->
@ -760,76 +760,76 @@ class="form-control input-reply-textarea">{!! $automatic_reply_text->value ?? ''
<!-- </div>-->
<!-- </div>-->
<!--</div>-->
</div>
<h3>Effect</h3>
<p>The effect describes what should happen when the filter above matches. It happens
automatically, before the e-mail shows up in
the dashboard.</p>
<div class="dev-content-schedule">
<label>Assign To</label>
<div class="schedule-box">
<select name="assign_to">
@foreach($company_users as $company_user)
<option value="{{$company_user->user->id}}" @if($rule && !is_null($rule->assign_to) && $rule->assign_to == $company_user->user->id) selected @endif>{{$company_user->user->name}}</option>
@endforeach
</select>
</div>
</div>
<div class="dev-form-inner">
<div class="col-left">
<div class="dev-input-group dev-input-group-input-info">
<label>Message to assigned editor</label>
<textarea rows="6" name="message_to_assigned_editor">{{$rule->message_to_assigned_editor ?? ''}}</textarea>
</div>
<!-- </div>-->
<!-- <h3>Effect</h3>-->
<!-- <p>The effect describes what should happen when the filter above matches. It happens-->
<!-- automatically, before the e-mail shows up in-->
<!-- the dashboard.</p>-->
<!-- <div class="dev-content-schedule">-->
<!-- <label>Assign To</label>-->
<!-- <div class="schedule-box">-->
<!-- <select name="assign_to">-->
<!-- @foreach($company_users as $company_user)-->
<!-- <option value="{{$company_user->user->id}}" @if($rule && !is_null($rule->assign_to) && $rule->assign_to == $company_user->user->id) selected @endif>{{$company_user->user->name}}</option>-->
<!-- @endforeach-->
<!-- </select>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="dev-form-inner">-->
<!-- <div class="col-left">-->
<!-- <div class="dev-input-group dev-input-group-input-info">-->
<!-- <label>Message to assigned editor</label>-->
<!-- <textarea rows="6" name="message_to_assigned_editor">{{$rule->message_to_assigned_editor ?? ''}}</textarea>-->
<!-- </div>-->
<!--<div class="dev-input-group">-->
<!-- <label class="dev-checkbox-wrapper">Remove and hide from the statistics -->
<!-- <input type="checkbox">-->
<!-- <span class="checkmark"></span>-->
<!-- </label>-->
<!--</div>-->
</div>
<div class="col-right">
<div class="dev-content-schedule">
<label>Add tags</label>
<div class="schedule-box">
<select name="tag_id">
@foreach($tags as $tag)
<option value="{{$tag->id}}" @if($rule && !is_null($rule->tag_id) && $rule->tag_id == $tag->id) selected @endif>{{$tag->name}}</option>
@endforeach
</select>
</div>
</div>
<div class="checkbox-box">
<div class="dev-input-group">
<input type="radio" value="set as done" name="status" @if($rule && !is_null($rule->status) && $rule->status == 'set as done') checked @endif>
<label class="dev-checkbox-wrapper">Set as done</label>
</div>
<div class="dev-input-group">
<label class="dev-checkbox-wrapper">Set highest priority</label>
<input type="radio" value="Set highest priority" name="priority" @if($rule && !is_null($rule->priority) && $rule->priority == 'Set highest priority') checked @endif>
</div>
<div class="dev-input-group">
<label class="dev-checkbox-wrapper">Mark as spam</label>
<input type="radio" value="mask as spam" name="status" @if($rule && !is_null($rule->status) && $rule->status == 'mask as spam') checked @endif>
</div>
<div class="dev-input-group">
<label class="dev-checkbox-wrapper">Set lowest priority</label>
<input type="radio" value="Set lowest priority" name="priority" @if($rule && !is_null($rule->priority) && $rule->priority == 'Set lowest priority') checked @endif>
</div>
</div>
</div>
</div>
<!-- </div>-->
<!-- <div class="col-right">-->
<!-- <div class="dev-content-schedule">-->
<!-- <label>Add tags</label>-->
<!-- <div class="schedule-box">-->
<!-- <select name="tag_id">-->
<!-- @foreach($tags as $tag)-->
<!-- <option value="{{$tag->id}}" @if($rule && !is_null($rule->tag_id) && $rule->tag_id == $tag->id) selected @endif>{{$tag->name}}</option>-->
<!-- @endforeach-->
<!-- </select>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="checkbox-box">-->
<!-- <div class="dev-input-group">-->
<!-- <input type="radio" value="set as done" name="status" @if($rule && !is_null($rule->status) && $rule->status == 'set as done') checked @endif>-->
<!-- <label class="dev-checkbox-wrapper">Set as done</label>-->
<!-- </div>-->
<!-- <div class="dev-input-group">-->
<!-- <label class="dev-checkbox-wrapper">Set highest priority</label>-->
<!-- <input type="radio" value="Set highest priority" name="priority" @if($rule && !is_null($rule->priority) && $rule->priority == 'Set highest priority') checked @endif>-->
<!-- </div>-->
<!-- <div class="dev-input-group">-->
<!-- <label class="dev-checkbox-wrapper">Mark as spam</label>-->
<!-- <input type="radio" value="mask as spam" name="status" @if($rule && !is_null($rule->status) && $rule->status == 'mask as spam') checked @endif>-->
<!-- </div>-->
<!-- <div class="dev-input-group">-->
<!-- <label class="dev-checkbox-wrapper">Set lowest priority</label>-->
<!-- <input type="radio" value="Set lowest priority" name="priority" @if($rule && !is_null($rule->priority) && $rule->priority == 'Set lowest priority') checked @endif>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<button type="submit" class="dev-form-submit-btn">Save</button>
</form>
</div>
<!-- <button type="submit" class="dev-form-submit-btn">Save</button>-->
<!-- </form>-->
<!-- </div>-->
</div>
<!--</div>-->
<!-- -->
<div class="dev-tabcontent dev-tabcontent-canned">
<div class="dev-tabcontent-outers">

View File

@ -190,13 +190,7 @@ function updateStatusOptions(selectedFilter) {
@foreach($company_users as $company_user)
options += '<option value="{{ $company_user->user->id }}">{{ $company_user->user->name }}</option>';
@endforeach
// $('#status-select').html(`
// <option disabled value="">Select Users</option>`
// @foreah($company_users as $company_user)
// `<option value="`{{$company_user->user->id}}`">`{{$company_user->user->name}}`</option>
// <option value="Abdullah">Abdullah</option>
// `);
// Update the select element with the generated options
$('#status-select').html(options);
$('.filter_based__data').show();
break;
@ -395,6 +389,76 @@ function updateStatusOptions(selectedFilter) {
/* Show checkboxes when 'Handle Multiple' is active */
.handle-multiple-active .checkbox-wrapper {
display: block;
}
.chat-content {
padding: 12px 11px !important;
}
.chat-user-img{
margin-left:12px !important;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
/* When checkbox is checked, set background to green */
input[type="checkbox"]:checked {
background-color: #748C62;
border-color: #748C62;
}
/* Optional: Add checkmark icon or any visual effect */
input[type="checkbox"]:checked::before {
transform: translate(0px, -1px);
content: '✔';
display: block;
text-align: center;
color: white;
font-weight: bold;
font-size: 13px;
margin-bottom: -1px;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
margin-right: 7px;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
/* margin-top: 12px; */
transform: translate(2px, 6px);
}
</style>
<div class="content-wrapper">
@ -625,7 +689,6 @@ function fetchTickets() {
});
</script>
<x-custom-modals />

View File

@ -10,7 +10,7 @@
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>
<!--<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>-->
<!-- Bootstrap Styles -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
@ -50,65 +50,21 @@
</div>
<!-- Jquery -->
<!-- <script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<!-- Bootstrap scripts -->
<!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script> -->
crossorigin="anonymous"></script>
<!-- Fomantic Ui Scripts -->
<!-- <script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>
<!-- Main Custom Js -->
<script src="{{ asset('assets/script.js') }}"></script>
<script>document.querySelector('.input-action img[alt="Attachment"]').addEventListener('click', function() {
document.getElementById('file-picker').click();
});
</script>
<style>
.outer-message-input-box {
width: 100%;
padding: 10px;
background-color: #f8f9fa;
}
.inner-message-input {
width: 100%;
background-color: #ffffff;
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.inner-message-input input {
flex-grow: 1;
margin-right: 10px;
}
.input-action img {
margin-left: 10px;
cursor: pointer;
}
.input-action .file-picker {
display: none;
}
.fa-paper-plane-o {
font-size: 17px !important;
background: #748C62 !important;
padding: 3px 7px !important;
color: white !important;
border-radius: 3px !important;
cursor: pointer !important;
}
</style>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

View File

@ -24,6 +24,15 @@
<!-- Main Styles -->
<link rel="stylesheet" href="{{ asset('assets/style.css') }}">
<link rel="icon" href="{{asset('images/favicon.ico')}}" type="image/png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
<style>
.logo_be .img{
transform: translate(7px, -5px) !important;
}
</style>
</head>
</head>
@ -34,9 +43,9 @@
<!-- Sidebar -->
<div class=" sidebar-area short-sidebar">
<aside class="bg-dark-green-color align-items-center">
<div class="image-box d-flex justify-content-between">
<img src="{{ asset('images/logo-white.png') }}" alt="Site Logo">
<aside class="bg-dark-green-color align-items-center ">
<div class="image-box d-flex justify-content-between logo_be">
<img src="{{ asset('images/logo_cropped.png') }}" class="img" alt="Site Logo">
<div class="dev-toggle-sidebar dev-close"><img src="{{ asset('images/icons/close-icon.svg') }}" alt=""></div>
</div>
<div class="side-bar-links d-flex justify-content-center">

View File

@ -131,15 +131,15 @@
<?php
$tags = [];
$db_tags = getTicketMeta($ticket->id,'tags');
$db_tags = getTicketMeta($ticket->id,'tags',false);
if($db_tags){
// if($db_tags){
foreach($db_tags as $tag){
$tags[] = $tag->value;
}
// foreach($db_tags as $tag){
// $tags[] = $tag->value;
// }
}
// }
?>
<input type="text" name="tags" id="tags" value="{{implode(',',$tags)}}" placeholder="Type and press Enter"

View File

@ -1,3 +1,65 @@
<style>
.attachment {
border: 1px solid #ccc;
padding: 10px;
margin-top: 10px;
}
.chat-sender .single-message-chat{
display: flex;
justify-content: flex-end;
}
.chat-sender .single-message-chat{
text-align:right;
}
.chat_style p{
text-align:left !important;
}
.single-message-chat img {
width: 83% !important;
height: auto !important;
max-height:200px;
}
.left_box img {
width: 62% !important;
height: auto !important;
}
.left_box p{
color: rgba(117, 138, 137, 1);
font-family: "Satoshi", sans-serif;
font-size: 10px;
transform: translate(8px, 2px);
}
#cannedResponseModal ul {
list-style: none;
padding: 0;
display: flex !important;
gap: 8px !important;
flex-wrap: wrap !important;
}
.canned-response {
width: 100%;
padding: 10px;
font-size: 16px;
border: none;
background-color: #748C62 !important;
color: white;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s, box-shadow 0.3s;
}
.single-message-chat {
width: 100%;
height: -webkit-fill-available !important;
}
</style>
@foreach($messages as $message)
<!-- Recepient Message -->
@if($message->user_id == 0)
@ -6,7 +68,19 @@
<img src="{{ asset('images/Avatar.png') }}" alt="User">
</div>
<div class="single-message-chat">
<div class="user-message">{!!$message->message!!}</div>
<div class="user-message">
{!!$message->message!!}
@if(!is_null($message->attachments) && count(json_decode($message->attachments)) > 0)
@foreach(json_decode($message->attachments) as $attachment)
<div class="attachment">
@php
$fileName = basename($attachment);
@endphp
<a download href="{{ url('' . $attachment) }}">{{$fileName}}</a>
</div>
@endforeach
@endif
</div>
<p class="message-time">{{ \Carbon\Carbon::parse($message->created_at)->format('h:i A') }}</p>
</div>
</div>
@ -14,16 +88,15 @@
@elseif($message->user_id == 1)
<div class="chat-message chat-sender d-flex justify-content-end">
<div class="single-message-chat">
@if(!containsHtml($message->message))
<p class="user-message bg-light-green-color color-light">{!!$message->message!!}</p>
@else
{!! $message->message !!}
@endif
<div class="user-message bg-light-green-color color-light chat_style">{!!$message->message!!}</div>
<div class="chat-user-img-box align-self-end left_box">
<img src="{{ asset('images/Avatar.png') }}" alt="User">
<p class="message-time">{{ \Carbon\Carbon::parse($message->created_at)->format('h:i A') }}</p>
</div>
<div class="chat-user-img-box align-self-end">
<img src="{{ asset('images/Avatar.png') }}" alt="User">
</div>
</div>
@endif
@endforeach

View File

@ -4,6 +4,38 @@
@section('content')
<!-- Update Profile --->
<div class="content-wrapper">
<div class="dev-chat-tabs">
<div class="dev-tabcontent dev-tabcontent-users">
<div class="dev-tabcontent-outers">
<div class="dev-title-row">
<h2>Update Profile</h2>
</div>
<form method="POST" action="{{ route('update.profile') }}" enctype="multipart/form-data">
@csrf
<div class="dev-input-group">
<label for="name">Name</label>
<input name="name" value="{{$user->name}}" type="text" placeholder="Enter your name" required>
</div>
<div class="dev-input-group">
<label for="email">Email</label>
<input name="email" type="email" readonly value="{{$user->email}}" placeholder="Enter your email" required>
</div>
<div class="dev-input-group">
<label for="email">Profile Image</label>
<input name="profile_image" type="file">
</div>
<button type="submit">Update</button>
</form>
</div>
</div>
</div>
</div>
<!-- End Update Profile -->
<div class="content-wrapper">
<div class="dev-chat-tabs">
<div class="dev-tabcontent dev-tabcontent-users">

View File

@ -7,6 +7,11 @@
<style>
div.chat-inbox>.chat-content-wrapper>.chat-message>.single-message-chat>.user-message{
max-width:90%!important;
width: 70%;
}
.single-message-chat{
width:100%;
}
.receiver-message{
@ -16,6 +21,18 @@
width: 100% !important;
height: auto !important;
}
.close-button {
color: #aaa;
/* float: right; */
font-size: 28px;
font-weight: bold;
background: ;
display: flex !important;
justify-content: flex-end !important;
margin-top: -14px;
font-size: 32px;
}
</style>
<input type="hidden" value="{{$single_ticket->id}}" id="aw-ticket_id"/>
@ -133,17 +150,37 @@ class="form-control input-reply-textarea" id="editor1" required></textarea>
CKEDITOR.plugins.add('addcannedresponse', {
init: function(editor) {
// Command for inserting canned response
editor.addCommand('addCannedResponseCmd', {
exec: function(editor) {
// Show your modal or handle canned response insertion
document.getElementById('cannedResponseModal').style.display = 'block';
}
});
// Command for inserting signature
editor.addCommand('addSignatureCmd', {
exec: function(editor) {
var signatureHtml = `<br>{!! $email_signature !!}`; // Signature content
CKEDITOR.instances.editor1.insertHtml(signatureHtml);
}
});
// Add "Insert Canned Response" button
editor.ui.addButton('AddCannedResponse', {
label: 'Insert Canned Response',
command: 'addCannedResponseCmd',
icon: 'https://kundesone.no/images/canned.png', // Use an accessible icon URL or local path
toolbar: 'insert,0'
});
// Add "Insert Signature" button
editor.ui.addButton('AddSignature', {
label: 'Insert Signature',
command: 'addSignatureCmd',
icon: 'https://kundesone.no/images/signature-icon.png', // Placeholder icon URL, replace with a valid one
toolbar: 'insert,1'
});
}
});

View File

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Terms And Conditions</title>
</head>
<body>
<div>
{!! $link_text !!}
</div>
</body>
</html>

View File

@ -152,8 +152,68 @@
display: block;
}
.chat-content {
padding: 12px 11px !important;
}
.chat-user-img{
margin-left:12px !important;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
/* When checkbox is checked, set background to green */
input[type="checkbox"]:checked {
background-color: #748C62;
border-color: #748C62;
}
/* Optional: Add checkmark icon or any visual effect */
input[type="checkbox"]:checked::before {
transform: translate(0px, -1px);
content: '✔';
display: block;
text-align: center;
color: white;
font-weight: bold;
font-size: 13px;
margin-bottom: -1px;
}
input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
margin-right: 7px;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
}
.handle_multiple__options label {
display: flex;
margin-bottom: 10px;
align-items: flex-start;
/* margin-top: 12px; */
transform: translate(2px, 6px);
}
</style>
<div class="content-wrapper">

View File

@ -25,8 +25,6 @@
|
*/
Route::get('/', [LoginController::class, 'login'])->name('login.create');
Route::post('store/login', [LoginController::class, 'storeLogin'])->name('store.login');
Route::post('store/register', [RegisterController::class, 'storeRegister'])->name('store.register');
@ -57,6 +55,7 @@
Route::get('/dashboard', [DashboardController::class, 'dashboard'])->name('index')->middleware('verifyDomain');;
Route::get('/profile', [DashboardController::class, 'profile'])->name('profile');
Route::post('update/profile', [DashboardController::class, 'updateProfile'])->name('update.profile');
Route::get('company-info', [CompanyController::class, 'getCompanyInfo'])->name('get.company.info');
Route::get('/waiting', [TicketController::class, 'waiting'])->name('waiting');
Route::get('/all-tickets', [TicketController::class, 'allTickets'])->name('all.tickets');
@ -104,6 +103,7 @@
Route::post('store/chat-canned-responses', [ChatSettingController::class, 'storeChatCannedResponses'])->name('store.chat.canned.responses');
Route::get('delete/chat-canned-responses/{id}', [ChatSettingController::class, 'deleteChatCannedResponses'])->name('delete.chat.canned.responses');
Route::post('store/personal-data', [ChatSettingController::class, 'storePersonalData'])->name('store.personal.data');
Route::get('terms-and-conditions/{companyId}', [ChatSettingController::class, 'companyTermsAndConditions'])->name('company.terms.conditions');
Route::post('store/tags', [ChatSettingController::class, 'storeTags'])->name('store.tags');
Route::post('setting/all-chat', [ChatSettingController::class, 'settingAllChat'])->name('setting.all.chat');
Route::post('block/ip-addresses', [ChatSettingController::class, 'blockIpAdresses'])->name('block.ip.addresses');