=Live version
This commit is contained in:
parent
06ba160e4a
commit
3d05d7639f
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 9436f7a699484438f4beff38bf1c9a7372776fd2
|
||||||
|
|
@ -26,7 +26,7 @@ class SendReminder extends Command
|
||||||
$sends = DB::table('message_reminder as ms')
|
$sends = DB::table('message_reminder as ms')
|
||||||
->leftJoin('message_template as t', 'ms.template', '=', 't.id')
|
->leftJoin('message_template as t', 'ms.template', '=', 't.id')
|
||||||
->where('ms.status', 'active')
|
->where('ms.status', 'active')
|
||||||
->select('ms.*', 't.template_code', 't.template_body', 't.title as template_title')
|
->select('ms.*', 't.template_code', 't.template_body', 't.title as template_title', 't.button_url', 't.button_label')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$sessions = DB::table('sessions')
|
$sessions = DB::table('sessions')
|
||||||
|
|
@ -62,23 +62,28 @@ class SendReminder extends Command
|
||||||
[
|
[
|
||||||
'source' => 'dynamodb',
|
'source' => 'dynamodb',
|
||||||
'search' => json_encode($searchparam),
|
'search' => json_encode($searchparam),
|
||||||
'fields' => json_encode(['session_code', 'email', 'name', 'phone', 'id']),
|
'start' => 0,
|
||||||
|
'length' => 9999,
|
||||||
],
|
],
|
||||||
env('LAMBDA_ACCESS_TOKEN', 'default_value')
|
env('LAMBDA_ACCESS_TOKEN', 'default_value')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sizeof($response->data->items) > 0) {
|
$contacts = SessionCommunicationService::extractContactItems($response);
|
||||||
|
|
||||||
|
if (count($contacts) > 0) {
|
||||||
$config = [
|
$config = [
|
||||||
'id' => $send->id,
|
'id' => $send->id,
|
||||||
'template_code' => $send->template_code,
|
'template_code' => $send->template_code,
|
||||||
'template_body' => $send->template_body,
|
'template_body' => $send->template_body,
|
||||||
'template_title' => $send->template_title,
|
'template_title' => $send->template_title,
|
||||||
|
'button_url' => $send->button_url,
|
||||||
|
'button_label' => $send->button_label,
|
||||||
'header_params' => $send->header_params,
|
'header_params' => $send->header_params,
|
||||||
'body_params' => $send->body_params,
|
'body_params' => $send->body_params,
|
||||||
'button_params' => $send->button_params,
|
'button_params' => $send->button_params,
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($response->data->items as $item) {
|
foreach ($contacts as $item) {
|
||||||
$this->comm->dispatchForContact($session, $item, $config);
|
$this->comm->dispatchForContact($session, $item, $config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Http\Requests\LambdaRequest;
|
use App\Http\Requests\LambdaRequest;
|
||||||
|
use App\Models\Session;
|
||||||
use App\Services\SessionCommunicationService;
|
use App\Services\SessionCommunicationService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
@ -29,6 +30,18 @@ class Sendwhatsapp extends Command
|
||||||
$sends = DB::table('raphiwa_send')->where('is_sent', 0)->limit(30)->get();
|
$sends = DB::table('raphiwa_send')->where('is_sent', 0)->limit(30)->get();
|
||||||
if ($sends->count() > 0) {
|
if ($sends->count() > 0) {
|
||||||
foreach ($sends as $send) {
|
foreach ($sends as $send) {
|
||||||
|
if ($send->session_id > 0) {
|
||||||
|
$mode = $this->comm->resolveMode((int) $send->session_id);
|
||||||
|
if (!$this->comm->shouldSendWhatsApp($mode)) {
|
||||||
|
DB::table('raphiwa_send')->where('id', $send->id)->update([
|
||||||
|
'is_sent' => 1,
|
||||||
|
'response' => 'skipped_email_only_session',
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$resp = Http::withHeaders(['Authorization' => $auth])
|
$resp = Http::withHeaders(['Authorization' => $auth])
|
||||||
->post('https://api.interakt.ai/v1/public/message/', (array) json_decode($send->text));
|
->post('https://api.interakt.ai/v1/public/message/', (array) json_decode($send->text));
|
||||||
$respj = json_decode($resp->getBody());
|
$respj = json_decode($resp->getBody());
|
||||||
|
|
@ -65,6 +78,8 @@ class Sendwhatsapp extends Command
|
||||||
't.template_code',
|
't.template_code',
|
||||||
't.template_body',
|
't.template_body',
|
||||||
't.title as template_title',
|
't.title as template_title',
|
||||||
|
't.button_url',
|
||||||
|
't.button_label',
|
||||||
'ms.header_params',
|
'ms.header_params',
|
||||||
'ms.body_params',
|
'ms.body_params',
|
||||||
'ms.button_params',
|
'ms.button_params',
|
||||||
|
|
@ -88,24 +103,30 @@ class Sendwhatsapp extends Command
|
||||||
[
|
[
|
||||||
'source' => 'dynamodb',
|
'source' => 'dynamodb',
|
||||||
'search' => json_encode($searchparam),
|
'search' => json_encode($searchparam),
|
||||||
'fields' => json_encode(['session_code', 'email', 'name', 'phone', 'id']),
|
'start' => 0,
|
||||||
|
'length' => 9999,
|
||||||
],
|
],
|
||||||
env('LAMBDA_ACCESS_TOKEN', 'default_value')
|
env('LAMBDA_ACCESS_TOKEN', 'default_value')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sizeof($response->data->items) > 0) {
|
$sessionModel = Session::find($send->session_id);
|
||||||
|
$contacts = SessionCommunicationService::extractContactItems($response);
|
||||||
|
|
||||||
|
if ($sessionModel && count($contacts) > 0) {
|
||||||
$config = [
|
$config = [
|
||||||
'id' => $send->id,
|
'id' => $send->id,
|
||||||
'template_code' => $send->template_code,
|
'template_code' => $send->template_code,
|
||||||
'template_body' => $send->template_body,
|
'template_body' => $send->template_body,
|
||||||
'template_title' => $send->template_title,
|
'template_title' => $send->template_title,
|
||||||
|
'button_url' => $send->button_url,
|
||||||
|
'button_label' => $send->button_label,
|
||||||
'header_params' => $send->header_params,
|
'header_params' => $send->header_params,
|
||||||
'body_params' => $send->body_params,
|
'body_params' => $send->body_params,
|
||||||
'button_params' => $send->button_params,
|
'button_params' => $send->button_params,
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($response->data->items as $item) {
|
foreach ($contacts as $item) {
|
||||||
$this->comm->dispatchForContact($send, $item, $config);
|
$this->comm->dispatchForContact($sessionModel, $item, $config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,18 @@ class MicrositeController extends LocalController
|
||||||
|
|
||||||
public function register($code, Request $request)
|
public function register($code, Request $request)
|
||||||
{
|
{
|
||||||
$session = (object)Session::where('code', $code)->first()->toArray();
|
$sessionModel = Session::where('code', $code)->first();
|
||||||
|
$session = (object) $sessionModel->toArray();
|
||||||
$linked_user = $request->input('linked_user', false);
|
$linked_user = $request->input('linked_user', false);
|
||||||
$session->dates = json_decode($session->dates);
|
$session->dates = json_decode($session->dates);
|
||||||
$request->session()->put('session_type', $session->type);
|
$request->session()->put('session_type', $session->type);
|
||||||
$request->session()->put('linked_user', $linked_user);
|
$request->session()->put('linked_user', $linked_user);
|
||||||
//echo $session->type; exit;
|
$comm = app(SessionCommunicationService::class);
|
||||||
return view($session->type . '.register', ['session' => $session]);
|
|
||||||
|
return view($session->type . '.register', [
|
||||||
|
'session' => $session,
|
||||||
|
'communication_mode' => $comm->resolveMode($sessionModel),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postregister($code, Request $request)
|
public function postregister($code, Request $request)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class MsgreminderController extends LocalController
|
||||||
foreach($sessions as $session){
|
foreach($sessions as $session){
|
||||||
$session_bb[$session->id] = $session->type.' ('.$session->number.') '. date("d-m-Y", strtotime($session->start_date)) . ' (' . $session->venue. ')';
|
$session_bb[$session->id] = $session->type.' ('.$session->number.') '. date("d-m-Y", strtotime($session->start_date)) . ' (' . $session->venue. ')';
|
||||||
}
|
}
|
||||||
$data = MessageTemplate::where('status', 'active')->select('id', 'title', 'template_body', 'template_code', 'header_params', 'body_params', 'button_params')->get();
|
$data = MessageTemplate::where('status', 'active')->select('id', 'title', 'template_body', 'template_code', 'header_params', 'body_params', 'button_params', 'button_url', 'button_label')->get();
|
||||||
$templates = [];
|
$templates = [];
|
||||||
$template_data = [];
|
$template_data = [];
|
||||||
foreach($data as $d) {
|
foreach($data as $d) {
|
||||||
|
|
@ -57,6 +57,7 @@ class MsgreminderController extends LocalController
|
||||||
'header_params' => ['title' => 'Header Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'header_params' => ['title' => 'Header Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'body_params' => ['title' => 'Body Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'body_params' => ['title' => 'Body Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'button_params' => ['title' => 'Button Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'button_params' => ['title' => 'Button Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
|
'button_cta' => ['title' => 'Email CTA (from template)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'days_before' => ['title' => 'Days Before', 'type' => 'number', 'required' => true],
|
'days_before' => ['title' => 'Days Before', 'type' => 'number', 'required' => true],
|
||||||
'time' => ['title' => 'Time of Day', 'type' => 'time', 'required' => true],
|
'time' => ['title' => 'Time of Day', 'type' => 'time', 'required' => true],
|
||||||
'audience' => ['title' => 'Audience', 'type' => 'select', 'values' => ['all' => 'All', 'attended' => 'Only Attended'] , 'required' => true],
|
'audience' => ['title' => 'Audience', 'type' => 'select', 'values' => ['all' => 'All', 'attended' => 'Only Attended'] , 'required' => true],
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class MsgsendController extends LocalController
|
||||||
$session_bb[$session->id] = $session->type.' ('.$session->number.') '. date("d-m-Y", strtotime($session->start_date)) . ' (' . $session->venue. ')';
|
$session_bb[$session->id] = $session->type.' ('.$session->number.') '. date("d-m-Y", strtotime($session->start_date)) . ' (' . $session->venue. ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = MessageTemplate::where('status', 'active')->select('id', 'title', 'template_body', 'template_code', 'header_params', 'body_params', 'button_params')->get();
|
$data = MessageTemplate::where('status', 'active')->select('id', 'title', 'template_body', 'template_code', 'header_params', 'body_params', 'button_params', 'button_url', 'button_label')->get();
|
||||||
$templates = [];
|
$templates = [];
|
||||||
$template_data = [];
|
$template_data = [];
|
||||||
foreach($data as $d) {
|
foreach($data as $d) {
|
||||||
|
|
@ -94,6 +94,7 @@ class MsgsendController extends LocalController
|
||||||
'header_params' => ['title' => 'Header Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'header_params' => ['title' => 'Header Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'body_params' => ['title' => 'Body Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'body_params' => ['title' => 'Body Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'button_params' => ['title' => 'Button Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
'button_params' => ['title' => 'Button Params (only if changes needed)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
|
'button_cta' => ['title' => 'Email CTA (from template)', 'type' => 'keywords', 'fullwidth' => true],
|
||||||
'audience' => ['title' => 'Audience', 'type' => 'select', 'values' => ['all' => 'All', 'attended' => 'Only Attended'] , 'required' => true],
|
'audience' => ['title' => 'Audience', 'type' => 'select', 'values' => ['all' => 'All', 'attended' => 'Only Attended'] , 'required' => true],
|
||||||
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
|
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,9 @@ class MsgtemplateController extends LocalController
|
||||||
'template_body' => ['title' => 'Template Body', 'type' => 'textarea', 'required' => true, 'fullwidth' => true],
|
'template_body' => ['title' => 'Template Body', 'type' => 'textarea', 'required' => true, 'fullwidth' => true],
|
||||||
'header_params' => ['title' => 'Header Params', 'type' => 'textarea', 'fullwidth' => true],
|
'header_params' => ['title' => 'Header Params', 'type' => 'textarea', 'fullwidth' => true],
|
||||||
'body_params' => ['title' => 'Body Params', 'type' => 'textarea', 'fullwidth' => true],
|
'body_params' => ['title' => 'Body Params', 'type' => 'textarea', 'fullwidth' => true],
|
||||||
'button_params' => ['title' => 'Button Params', 'type' => 'textarea', 'fullwidth' => true],
|
'button_params' => ['title' => 'Button Params (WhatsApp)', 'type' => 'textarea', 'fullwidth' => true],
|
||||||
//'email_body' => ['title' => 'Email Body', 'type' => 'textarea', 'required' => true, 'fullwidth' => true],
|
'button_url' => ['title' => 'Button URL (Email)', 'type' => 'text', 'fullwidth' => true, 'placeholder' => 'road.xltr.in/brochure/{{1}} or {{SESSION_CODE}}'],
|
||||||
|
'button_label' => ['title' => 'Button Label (Email)', 'type' => 'text', 'fullwidth' => true, 'placeholder' => 'Complete feedback'],
|
||||||
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
|
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
|
||||||
];
|
];
|
||||||
return $fields;
|
return $fields;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Services\RegisterLocationService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class RegisterLocationController extends Controller
|
||||||
|
{
|
||||||
|
public function countries()
|
||||||
|
{
|
||||||
|
$paths = [
|
||||||
|
storage_path('app/geo/countriesAndStates.json'),
|
||||||
|
public_path('assets/data/countriesAndStates.json'),
|
||||||
|
public_path('data/countriesAndStates.json'),
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (is_readable($path)) {
|
||||||
|
return response()->file($path, [
|
||||||
|
'Content-Type' => 'application/json; charset=UTF-8',
|
||||||
|
'Cache-Control' => 'public, max-age=86400',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(['error' => 'Countries data not found'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cities(Request $request, RegisterLocationService $locations)
|
||||||
|
{
|
||||||
|
$country = (string) $request->query('country', '');
|
||||||
|
$state = (string) $request->query('state', '');
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
$locations->getCitiesForCountryState($country, $state)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,8 @@ class MessageTemplate extends Model
|
||||||
'header_params',
|
'header_params',
|
||||||
'body_params',
|
'body_params',
|
||||||
'button_params',
|
'button_params',
|
||||||
|
'button_url',
|
||||||
|
'button_label',
|
||||||
'status',
|
'status',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
class RegisterLocationService
|
||||||
|
{
|
||||||
|
private const SIGNUP_COUNTRY_TO_CSC_NAME = [
|
||||||
|
'Antigua and Barbuda' => 'Antigua And Barbuda',
|
||||||
|
'Eswatini' => 'Swaziland',
|
||||||
|
'Guernsey' => 'Guernsey and Alderney',
|
||||||
|
'Ivory Coast' => "Cote D'Ivoire (Ivory Coast)",
|
||||||
|
'North Macedonia' => 'Macedonia',
|
||||||
|
'Papua New Guinea' => 'Papua new Guinea',
|
||||||
|
'Saint Kitts and Nevis' => 'Saint Kitts And Nevis',
|
||||||
|
'Saint Vincent and the Grenadines' => 'Saint Vincent And The Grenadines',
|
||||||
|
'Svalbard and Jan Mayen Islands' => 'Svalbard And Jan Mayen Islands',
|
||||||
|
'Timor-Leste' => 'East Timor',
|
||||||
|
'Trinidad and Tobago' => 'Trinidad And Tobago',
|
||||||
|
'Turks and Caicos Islands' => 'Turks And Caicos Islands',
|
||||||
|
'Wallis and Futuna Islands' => 'Wallis And Futuna Islands',
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $countries;
|
||||||
|
private static $states;
|
||||||
|
private static $cities;
|
||||||
|
|
||||||
|
public function getCitiesForCountryState(string $countryName, string $stateName): array
|
||||||
|
{
|
||||||
|
$countryName = trim($countryName);
|
||||||
|
$stateName = trim($stateName);
|
||||||
|
if ($countryName === '' || $stateName === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$countryCode = $this->resolveCountryCode($countryName);
|
||||||
|
if (!$countryCode) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$stateCode = $this->resolveStateCode($countryCode, $stateName);
|
||||||
|
if (!$stateCode) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = [];
|
||||||
|
foreach ($this->loadCities() as $row) {
|
||||||
|
if (!is_array($row) || count($row) < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (($row[1] ?? '') === $countryCode && ($row[2] ?? '') === $stateCode) {
|
||||||
|
$names[] = $row[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = array_values(array_unique($names));
|
||||||
|
sort($names, SORT_STRING);
|
||||||
|
|
||||||
|
return $names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveCountryCode(string $countryName): ?string
|
||||||
|
{
|
||||||
|
$lookup = self::SIGNUP_COUNTRY_TO_CSC_NAME[$countryName] ?? $countryName;
|
||||||
|
|
||||||
|
foreach ($this->loadCountries() as $country) {
|
||||||
|
if (!is_array($country)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $country['name'] ?? '';
|
||||||
|
if ($name === $lookup || $name === $countryName) {
|
||||||
|
return $country['isoCode'] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveStateCode(string $countryCode, string $stateName): ?string
|
||||||
|
{
|
||||||
|
foreach ($this->loadStates() as $state) {
|
||||||
|
if (!is_array($state)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (($state['countryCode'] ?? '') === $countryCode && ($state['name'] ?? '') === $stateName) {
|
||||||
|
return $state['isoCode'] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadCountries(): array
|
||||||
|
{
|
||||||
|
if (self::$countries === null) {
|
||||||
|
self::$countries = $this->readJson('country.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadStates(): array
|
||||||
|
{
|
||||||
|
if (self::$states === null) {
|
||||||
|
self::$states = $this->readJson('state.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$states;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadCities(): array
|
||||||
|
{
|
||||||
|
if (self::$cities === null) {
|
||||||
|
self::$cities = $this->readJson('city.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$cities;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readJson(string $filename): array
|
||||||
|
{
|
||||||
|
$path = storage_path('app/geo/' . $filename);
|
||||||
|
if (!is_readable($path)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode(file_get_contents($path), true);
|
||||||
|
|
||||||
|
return is_array($decoded) ? $decoded : [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,11 +2,15 @@
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Http\Requests\LambdaRequest;
|
||||||
use App\Models\Session;
|
use App\Models\Session;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class SessionCommunicationService
|
class SessionCommunicationService
|
||||||
{
|
{
|
||||||
|
public const EMAIL_SUBJECT_PREFIX = 'CREST Research and Training Support';
|
||||||
|
|
||||||
protected static $feedbackTemplateCodes = [
|
protected static $feedbackTemplateCodes = [
|
||||||
'crest_thankyou_and_feedback',
|
'crest_thankyou_and_feedback',
|
||||||
'crest_feedback_new',
|
'crest_feedback_new',
|
||||||
|
|
@ -14,17 +18,88 @@ class SessionCommunicationService
|
||||||
'pre_impact_assessment',
|
'pre_impact_assessment',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize Lambda contact/get_all response items to a foreach-safe array.
|
||||||
|
*/
|
||||||
|
public static function extractContactItems($response): array
|
||||||
|
{
|
||||||
|
if (!is_object($response) || !isset($response->data)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($response->data->items)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $response->data->items;
|
||||||
|
|
||||||
|
if (is_array($items)) {
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_object($items)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single contact object stored in items (not a list wrapper).
|
||||||
|
if (isset($items->id) && (isset($items->phone) || isset($items->name) || isset($items->email))) {
|
||||||
|
return [$items];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values((array) $items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolveSessionForMode($session): ?Session
|
||||||
|
{
|
||||||
|
if ($session instanceof Session) {
|
||||||
|
return $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($session)) {
|
||||||
|
return Session::find((int) $session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_object($session)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($session->session_id)) {
|
||||||
|
$loaded = Session::find($session->session_id);
|
||||||
|
if ($loaded) {
|
||||||
|
return $loaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($session->code)) {
|
||||||
|
$loaded = Session::where('code', $session->code)->first();
|
||||||
|
if ($loaded) {
|
||||||
|
return $loaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($session->id) && empty($session->template_code ?? null)) {
|
||||||
|
return Session::find($session->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function resolveMode($session): string
|
public function resolveMode($session): string
|
||||||
{
|
{
|
||||||
if (!$session) {
|
$sessionModel = $this->resolveSessionForMode($session);
|
||||||
|
|
||||||
|
if (!$sessionModel) {
|
||||||
|
if (is_object($session) && !empty($session->communication_mode)) {
|
||||||
|
$mode = strtolower(trim((string) $session->communication_mode));
|
||||||
|
if (in_array($mode, [Session::MODE_WHATSAPP, Session::MODE_EMAIL, Session::MODE_BOTH], true)) {
|
||||||
|
return $mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Session::MODE_WHATSAPP;
|
return Session::MODE_WHATSAPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mode = is_object($session)
|
$mode = strtolower(trim((string) ($sessionModel->communication_mode ?? '')));
|
||||||
? ($session->communication_mode ?? null)
|
|
||||||
: ($session['communication_mode'] ?? null);
|
|
||||||
|
|
||||||
$mode = strtolower(trim((string) $mode));
|
|
||||||
|
|
||||||
if (!in_array($mode, [Session::MODE_WHATSAPP, Session::MODE_EMAIL, Session::MODE_BOTH], true)) {
|
if (!in_array($mode, [Session::MODE_WHATSAPP, Session::MODE_EMAIL, Session::MODE_BOTH], true)) {
|
||||||
return Session::MODE_WHATSAPP;
|
return Session::MODE_WHATSAPP;
|
||||||
|
|
@ -33,6 +108,20 @@ class SessionCommunicationService
|
||||||
return $mode;
|
return $mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function resolveSessionId($session): int
|
||||||
|
{
|
||||||
|
$sessionModel = $this->resolveSessionForMode($session);
|
||||||
|
if ($sessionModel) {
|
||||||
|
return (int) $sessionModel->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($session)) {
|
||||||
|
return (int) ($session->session_id ?? $session->id ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) ($session['session_id'] ?? $session['id'] ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
public function shouldSendWhatsApp(string $mode): bool
|
public function shouldSendWhatsApp(string $mode): bool
|
||||||
{
|
{
|
||||||
return in_array($mode, [Session::MODE_WHATSAPP, Session::MODE_BOTH], true);
|
return in_array($mode, [Session::MODE_WHATSAPP, Session::MODE_BOTH], true);
|
||||||
|
|
@ -56,7 +145,8 @@ class SessionCommunicationService
|
||||||
|
|
||||||
public function buildSessionVariables($session, $sendRow = null): array
|
public function buildSessionVariables($session, $sendRow = null): array
|
||||||
{
|
{
|
||||||
$session = is_object($session) ? $session : (object) $session;
|
$sessionModel = $this->resolveSessionForMode($session);
|
||||||
|
$session = $sessionModel ?: (is_object($session) ? $session : (object) $session);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'SESSION_NAME' => $session->number ?? '',
|
'SESSION_NAME' => $session->number ?? '',
|
||||||
|
|
@ -95,11 +185,21 @@ class SessionCommunicationService
|
||||||
|
|
||||||
$sendId = is_object($sendRow) ? ($sendRow->id ?? 0) : ($sendRow['id'] ?? 0);
|
$sendId = is_object($sendRow) ? ($sendRow->id ?? 0) : ($sendRow['id'] ?? 0);
|
||||||
|
|
||||||
return array_merge($sessionVars, [
|
$vars = array_merge($sessionVars, [
|
||||||
'SESSION_CODE' => $sessionCode,
|
'SESSION_CODE' => $sessionCode,
|
||||||
'RECIPIENT_NAME' => is_object($contact) ? ($contact->name ?? '') : ($contact['name'] ?? ''),
|
'RECIPIENT_NAME' => is_object($contact) ? ($contact->name ?? '') : ($contact['name'] ?? ''),
|
||||||
'CERT_PARAM' => (is_object($contact) ? ($contact->phone ?? '') : ($contact['phone'] ?? '')) . '_' . $sendId,
|
'CERT_PARAM' => (is_object($contact) ? ($contact->phone ?? '') : ($contact['phone'] ?? '')) . '_' . $sendId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (is_object($contact)) {
|
||||||
|
foreach (get_object_vars($contact) as $key => $value) {
|
||||||
|
if (is_string($key) && $key !== '' && !is_array($value) && !is_object($value)) {
|
||||||
|
$vars[strtoupper($key)] = (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function substituteParams(?string $pipeList, array $vars): array
|
public function substituteParams(?string $pipeList, array $vars): array
|
||||||
|
|
@ -118,7 +218,7 @@ class SessionCommunicationService
|
||||||
return $params;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTemplateBody(string $body, array $vars): string
|
public function renderTemplateBody(string $body, array $vars, array $numericValues = []): string
|
||||||
{
|
{
|
||||||
$rendered = $body;
|
$rendered = $body;
|
||||||
|
|
||||||
|
|
@ -127,9 +227,31 @@ class SessionCommunicationService
|
||||||
$rendered = str_replace('{' . $key . '}', $value, $rendered);
|
$rendered = str_replace('{' . $key . '}', $value, $rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($numericValues as $index => $value) {
|
||||||
|
$placeholder = '{{' . ($index + 1) . '}}';
|
||||||
|
$rendered = str_replace($placeholder, (string) $value, $rendered);
|
||||||
|
}
|
||||||
|
|
||||||
return $rendered;
|
return $rendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure absolute URLs (e.g. road.xltr.in/path → https://road.xltr.in/path).
|
||||||
|
*/
|
||||||
|
public function normalizeUrlScheme(string $url): string
|
||||||
|
{
|
||||||
|
$url = trim($url);
|
||||||
|
if ($url === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('#^https?://#i', $url)) {
|
||||||
|
$url = 'https://' . ltrim($url, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
public function buildButtonSection(array $buttonValues): string
|
public function buildButtonSection(array $buttonValues): string
|
||||||
{
|
{
|
||||||
if (empty($buttonValues)) {
|
if (empty($buttonValues)) {
|
||||||
|
|
@ -152,6 +274,52 @@ class SessionCommunicationService
|
||||||
return implode("\n", $lines);
|
return implode("\n", $lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build email CTA button from template button_url / button_label + substituted params.
|
||||||
|
*/
|
||||||
|
public function buildEmailCallToAction(array $config, array $vars, array $buttonParamValues = []): string
|
||||||
|
{
|
||||||
|
$urlTemplate = trim($config['button_url'] ?? '');
|
||||||
|
$label = trim($config['button_label'] ?? '');
|
||||||
|
|
||||||
|
if ($urlTemplate === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->renderTemplateBody($urlTemplate, $vars, $buttonParamValues);
|
||||||
|
$url = $this->normalizeUrlScheme($url);
|
||||||
|
|
||||||
|
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
$paramValue = trim((string) ($buttonParamValues[0] ?? ''));
|
||||||
|
if ($paramValue !== '' && filter_var($this->normalizeUrlScheme($paramValue), FILTER_VALIDATE_URL)) {
|
||||||
|
$url = $this->normalizeUrlScheme($paramValue);
|
||||||
|
} elseif ($paramValue !== '') {
|
||||||
|
$base = $this->normalizeUrlScheme(
|
||||||
|
$this->renderTemplateBody(rtrim($urlTemplate, '/'), $vars, $buttonParamValues)
|
||||||
|
);
|
||||||
|
$url = rtrim($base, '/') . '/' . ltrim($paramValue, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->normalizeUrlScheme($url);
|
||||||
|
|
||||||
|
if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($label === '') {
|
||||||
|
$label = 'Click here';
|
||||||
|
}
|
||||||
|
$label = $this->renderTemplateBody($label, $vars, $buttonParamValues);
|
||||||
|
|
||||||
|
return '<table role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin:28px auto 8px;">'
|
||||||
|
. '<tr><td align="center" style="border-radius:6px;background-color:#1a3a5c;">'
|
||||||
|
. '<a href="' . e($url) . '" target="_blank" rel="noopener noreferrer" '
|
||||||
|
. 'style="display:inline-block;padding:14px 28px;font-size:15px;font-weight:bold;color:#ffffff;text-decoration:none;">'
|
||||||
|
. e($label)
|
||||||
|
. '</a></td></tr></table>';
|
||||||
|
}
|
||||||
|
|
||||||
public function buildInteraktTemplate(array $config, array $vars): array
|
public function buildInteraktTemplate(array $config, array $vars): array
|
||||||
{
|
{
|
||||||
if (!empty($config['header_values']) && is_array($config['header_values'])) {
|
if (!empty($config['header_values']) && is_array($config['header_values'])) {
|
||||||
|
|
@ -181,10 +349,9 @@ class SessionCommunicationService
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildEmailBody(string $templateBody, array $vars, array $buttonValues = [], array $headerValues = []): string
|
public function buildEmailBody(string $templateBody, array $vars, array $buttonValues = [], array $headerValues = [], array $numericValues = [], array $config = []): string
|
||||||
{
|
{
|
||||||
$body = $this->renderTemplateBody($templateBody, $vars);
|
$body = $this->renderTemplateBody($templateBody, $vars, $numericValues);
|
||||||
$body .= $this->buildButtonSection($buttonValues);
|
|
||||||
|
|
||||||
$imageHtml = '';
|
$imageHtml = '';
|
||||||
foreach ($headerValues as $headerValue) {
|
foreach ($headerValues as $headerValue) {
|
||||||
|
|
@ -193,31 +360,83 @@ class SessionCommunicationService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $imageHtml . nl2br(e($body));
|
$ctaHtml = $this->buildEmailCallToAction($config, $vars, $buttonValues);
|
||||||
|
if ($ctaHtml === '') {
|
||||||
|
$body .= $this->buildButtonSection($buttonValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $imageHtml . nl2br(e($body)) . $ctaHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildEmailSubject($session, $sendRow): string
|
public function buildEmailSubject($session, $sendRow): string
|
||||||
{
|
{
|
||||||
$sessionName = is_object($session) ? ($session->number ?? 'Crest Session') : ($session['number'] ?? 'Crest Session');
|
$sessionName = is_object($session) ? ($session->number ?? 'Session') : ($session['number'] ?? 'Session');
|
||||||
$title = is_object($sendRow) ? ($sendRow->template_title ?? null) : ($sendRow['template_title'] ?? null);
|
$title = is_object($sendRow) ? ($sendRow->template_title ?? null) : ($sendRow['template_title'] ?? null);
|
||||||
|
$prefix = self::EMAIL_SUBJECT_PREFIX;
|
||||||
|
|
||||||
if (!empty($title)) {
|
if (!empty($title)) {
|
||||||
return 'Crest – ' . $title;
|
return $prefix . ' – ' . $title;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Crest – ' . $sessionName;
|
return $prefix . ' – ' . $sessionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contactEmail($contact): ?string
|
public function contactEmail($contact): ?string
|
||||||
{
|
{
|
||||||
$email = is_object($contact) ? ($contact->email ?? '') : ($contact['email'] ?? '');
|
$candidates = [];
|
||||||
$email = trim((string) $email);
|
if (is_object($contact)) {
|
||||||
|
$candidates = [
|
||||||
|
$contact->email ?? null,
|
||||||
|
$contact->user_email ?? null,
|
||||||
|
$contact->Email ?? null,
|
||||||
|
];
|
||||||
|
} elseif (is_array($contact)) {
|
||||||
|
$candidates = [
|
||||||
|
$contact['email'] ?? null,
|
||||||
|
$contact['user_email'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($candidates as $email) {
|
||||||
|
$email = trim((string) $email);
|
||||||
|
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
return $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $email;
|
/**
|
||||||
|
* Load full contact from Lambda when get_all omitted email (fields projection).
|
||||||
|
*/
|
||||||
|
public function enrichContactEmail($contact)
|
||||||
|
{
|
||||||
|
if ($this->contactEmail($contact)) {
|
||||||
|
return $contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
$contactId = is_object($contact) ? ($contact->id ?? null) : ($contact['id'] ?? null);
|
||||||
|
if (!$contactId) {
|
||||||
|
return $contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = LambdaRequest::getrequest(
|
||||||
|
'contact',
|
||||||
|
'contact/get_one',
|
||||||
|
['id' => $contactId],
|
||||||
|
env('LAMBDA_ACCESS_TOKEN', 'default_value')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($response->data->item->email) && is_object($contact)) {
|
||||||
|
$contact->email = $response->data->item->email;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('enrichContactEmail failed for contact ' . $contactId . ': ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function queueWhatsApp($contact, int $sessionId, array $interaktPayload): void
|
public function queueWhatsApp($contact, int $sessionId, array $interaktPayload): void
|
||||||
|
|
@ -239,8 +458,14 @@ class SessionCommunicationService
|
||||||
|
|
||||||
public function queueEmail($contact, int $sessionId, string $subject, string $bodyHtml, ?string $templateCode = null): bool
|
public function queueEmail($contact, int $sessionId, string $subject, string $bodyHtml, ?string $templateCode = null): bool
|
||||||
{
|
{
|
||||||
|
$contact = $this->enrichContactEmail($contact);
|
||||||
$email = $this->contactEmail($contact);
|
$email = $this->contactEmail($contact);
|
||||||
if (!$email) {
|
if (!$email) {
|
||||||
|
$contactId = is_object($contact) ? ($contact->id ?? 'unknown') : 'unknown';
|
||||||
|
Log::info('email_send skipped: no valid email on contact', [
|
||||||
|
'contact_id' => $contactId,
|
||||||
|
'session_id' => $sessionId,
|
||||||
|
]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,9 +502,10 @@ class SessionCommunicationService
|
||||||
|
|
||||||
public function dispatchForContact($session, $contact, array $config): void
|
public function dispatchForContact($session, $contact, array $config): void
|
||||||
{
|
{
|
||||||
|
$sessionModel = $this->resolveSessionForMode($session);
|
||||||
$mode = $this->resolveMode($session);
|
$mode = $this->resolveMode($session);
|
||||||
$sessionId = is_object($session) ? (int) ($session->id ?? 0) : (int) ($session['id'] ?? 0);
|
$sessionId = $this->resolveSessionId($session);
|
||||||
$vars = $this->buildContactVariables($contact, $session, $config);
|
$vars = $this->buildContactVariables($contact, $sessionModel ?: $session, $config);
|
||||||
|
|
||||||
if (!empty($config['header_values']) && is_array($config['header_values'])) {
|
if (!empty($config['header_values']) && is_array($config['header_values'])) {
|
||||||
$headerParams = $config['header_values'];
|
$headerParams = $config['header_values'];
|
||||||
|
|
@ -307,10 +533,10 @@ class SessionCommunicationService
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->shouldSendEmail($mode)) {
|
if ($this->shouldSendEmail($mode)) {
|
||||||
$templateBody = $config['template_body'] ?? '';
|
$templateBody = trim($config['template_body'] ?? '');
|
||||||
if ($templateBody !== '') {
|
if ($templateBody !== '') {
|
||||||
$bodyHtml = $this->buildEmailBody($templateBody, $vars, $buttonParams, $headerParams);
|
$bodyHtml = $this->buildEmailBody($templateBody, $vars, $buttonParams, $headerParams, $bodyParams, $config);
|
||||||
$subject = $this->buildEmailSubject($session, $config);
|
$subject = $this->buildEmailSubject($sessionModel ?: $session, $config);
|
||||||
$this->queueEmail(
|
$this->queueEmail(
|
||||||
$contact,
|
$contact,
|
||||||
$sessionId,
|
$sessionId,
|
||||||
|
|
@ -318,6 +544,11 @@ class SessionCommunicationService
|
||||||
$bodyHtml,
|
$bodyHtml,
|
||||||
$config['template_code'] ?? null
|
$config['template_code'] ?? null
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
Log::warning('email_send skipped: empty template_body', [
|
||||||
|
'session_id' => $sessionId,
|
||||||
|
'template_code' => $config['template_code'] ?? null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddButtonUrlLabelToMessageTemplate extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('message_template')) {
|
||||||
|
if (!Schema::hasColumn('message_template', 'button_url')) {
|
||||||
|
Schema::table('message_template', function (Blueprint $table) {
|
||||||
|
$table->string('button_url', 500)->nullable()->after('button_params');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!Schema::hasColumn('message_template', 'button_label')) {
|
||||||
|
Schema::table('message_template', function (Blueprint $table) {
|
||||||
|
$table->string('button_label', 255)->nullable()->after('button_url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('message_template')) {
|
||||||
|
if (Schema::hasColumn('message_template', 'button_label')) {
|
||||||
|
Schema::table('message_template', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('button_label');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Schema::hasColumn('message_template', 'button_url')) {
|
||||||
|
Schema::table('message_template', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('button_url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,220 @@
|
||||||
|
/**
|
||||||
|
* Dependent country → state → city dropdowns for registration forms.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var CITIES_URL =
|
||||||
|
(typeof window !== 'undefined' && window.REGISTER_LOCATION_CITIES_URL) ||
|
||||||
|
'/geo/cities';
|
||||||
|
|
||||||
|
var countriesData = null;
|
||||||
|
|
||||||
|
function isValidCountriesMap(data) {
|
||||||
|
return (
|
||||||
|
data &&
|
||||||
|
typeof data === 'object' &&
|
||||||
|
!Array.isArray(data) &&
|
||||||
|
Object.keys(data).length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCountriesData() {
|
||||||
|
if (isValidCountriesMap(countriesData)) {
|
||||||
|
return Promise.resolve(countriesData);
|
||||||
|
}
|
||||||
|
if (isValidCountriesMap(window.REGISTER_LOCATION_COUNTRIES)) {
|
||||||
|
countriesData = window.REGISTER_LOCATION_COUNTRIES;
|
||||||
|
return Promise.resolve(countriesData);
|
||||||
|
}
|
||||||
|
return fetch('/geo/countries', { credentials: 'same-origin' })
|
||||||
|
.then(function (res) {
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('Failed to load countries data');
|
||||||
|
}
|
||||||
|
var ct = res.headers.get('content-type') || '';
|
||||||
|
if (ct.indexOf('json') === -1) {
|
||||||
|
throw new Error('Countries data returned non-JSON response');
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
|
if (!isValidCountriesMap(data)) {
|
||||||
|
throw new Error('Countries data is empty or invalid');
|
||||||
|
}
|
||||||
|
countriesData = data;
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCities(country, state) {
|
||||||
|
var url =
|
||||||
|
CITIES_URL +
|
||||||
|
'?country=' +
|
||||||
|
encodeURIComponent(country) +
|
||||||
|
'&state=' +
|
||||||
|
encodeURIComponent(state);
|
||||||
|
function fetchCitiesFrom(endpoint) {
|
||||||
|
return fetch(endpoint, { credentials: 'same-origin' }).then(function (res) {
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('Failed to load cities');
|
||||||
|
}
|
||||||
|
var ct = res.headers.get('content-type') || '';
|
||||||
|
if (ct.indexOf('json') === -1) {
|
||||||
|
throw new Error('Cities endpoint returned non-JSON response');
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchCitiesFrom(url).catch(function () {
|
||||||
|
if (url.indexOf('/api/geo/cities') === -1) {
|
||||||
|
return fetchCitiesFrom('/api/geo/cities' + url.substring(url.indexOf('?')));
|
||||||
|
}
|
||||||
|
throw new Error('Failed to load cities');
|
||||||
|
}).then(function (data) {
|
||||||
|
return Array.isArray(data) ? data : [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillSelect(select, options, placeholder) {
|
||||||
|
select.innerHTML = '';
|
||||||
|
var empty = document.createElement('option');
|
||||||
|
empty.value = '';
|
||||||
|
empty.textContent = placeholder;
|
||||||
|
select.appendChild(empty);
|
||||||
|
options.forEach(function (opt) {
|
||||||
|
var el = document.createElement('option');
|
||||||
|
el.value = opt;
|
||||||
|
el.textContent = opt;
|
||||||
|
select.appendChild(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCityMode(selectEl, inputEl, cityFieldName, mode, cities) {
|
||||||
|
cities = cities || [];
|
||||||
|
if (mode === 'select' && cities.length > 0) {
|
||||||
|
selectEl.style.display = '';
|
||||||
|
inputEl.style.display = 'none';
|
||||||
|
inputEl.removeAttribute('name');
|
||||||
|
inputEl.disabled = true;
|
||||||
|
inputEl.required = false;
|
||||||
|
selectEl.setAttribute('name', cityFieldName);
|
||||||
|
selectEl.disabled = false;
|
||||||
|
selectEl.required = true;
|
||||||
|
fillSelect(selectEl, cities, '-- Select City --');
|
||||||
|
} else {
|
||||||
|
selectEl.style.display = 'none';
|
||||||
|
inputEl.style.display = '';
|
||||||
|
selectEl.removeAttribute('name');
|
||||||
|
selectEl.disabled = true;
|
||||||
|
selectEl.required = false;
|
||||||
|
selectEl.value = '';
|
||||||
|
inputEl.setAttribute('name', cityFieldName);
|
||||||
|
inputEl.disabled = false;
|
||||||
|
inputEl.required = true;
|
||||||
|
inputEl.placeholder =
|
||||||
|
mode === 'select'
|
||||||
|
? 'Enter city (no list available for this state)'
|
||||||
|
: 'Enter city';
|
||||||
|
if (mode !== 'select') {
|
||||||
|
inputEl.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRegisterLocationDropdowns() {
|
||||||
|
var wrapper = document.querySelector('.register-location-fields');
|
||||||
|
var countrySelect = document.getElementById('country');
|
||||||
|
var stateSelect = document.getElementById('state');
|
||||||
|
var citySelect = document.getElementById('city');
|
||||||
|
var cityInput = document.querySelector('.register-location-city-input');
|
||||||
|
|
||||||
|
if (!wrapper || !countrySelect || !stateSelect || !citySelect || !cityInput) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cityFieldName = wrapper.getAttribute('data-city-field') || 'city';
|
||||||
|
|
||||||
|
return loadCountriesData().then(function (data) {
|
||||||
|
var countries = Object.keys(data).sort(function (a, b) {
|
||||||
|
return a.localeCompare(b);
|
||||||
|
});
|
||||||
|
fillSelect(countrySelect, countries, '-- Select Country --');
|
||||||
|
|
||||||
|
var defaultCountry = wrapper.getAttribute('data-default-country') || 'India';
|
||||||
|
if (defaultCountry && data[defaultCountry]) {
|
||||||
|
countrySelect.value = defaultCountry;
|
||||||
|
fillSelect(stateSelect, data[defaultCountry], '-- Select State --');
|
||||||
|
stateSelect.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
countrySelect.addEventListener('change', function () {
|
||||||
|
var country = countrySelect.value;
|
||||||
|
stateSelect.value = '';
|
||||||
|
citySelect.value = '';
|
||||||
|
cityInput.value = '';
|
||||||
|
setCityMode(citySelect, cityInput, cityFieldName, 'input');
|
||||||
|
citySelect.disabled = true;
|
||||||
|
cityInput.disabled = true;
|
||||||
|
|
||||||
|
if (!country || !data[country]) {
|
||||||
|
fillSelect(stateSelect, [], '-- Select State --');
|
||||||
|
stateSelect.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillSelect(stateSelect, data[country], '-- Select State --');
|
||||||
|
stateSelect.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
stateSelect.addEventListener('change', function () {
|
||||||
|
var country = countrySelect.value;
|
||||||
|
var state = stateSelect.value;
|
||||||
|
citySelect.value = '';
|
||||||
|
cityInput.value = '';
|
||||||
|
|
||||||
|
if (!country || !state) {
|
||||||
|
setCityMode(citySelect, cityInput, cityFieldName, 'input');
|
||||||
|
citySelect.disabled = true;
|
||||||
|
cityInput.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cityInput.disabled = true;
|
||||||
|
citySelect.disabled = true;
|
||||||
|
|
||||||
|
loadCities(country, state)
|
||||||
|
.then(function (cities) {
|
||||||
|
if (cities.length > 0) {
|
||||||
|
setCityMode(citySelect, cityInput, cityFieldName, 'select', cities);
|
||||||
|
} else {
|
||||||
|
setCityMode(citySelect, cityInput, cityFieldName, 'input');
|
||||||
|
cityInput.disabled = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
console.warn('City list unavailable, using text input', err);
|
||||||
|
setCityMode(citySelect, cityInput, cityFieldName, 'input');
|
||||||
|
cityInput.disabled = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (countrySelect.value && data[countrySelect.value]) {
|
||||||
|
stateSelect.dispatchEvent(new Event('change'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootRegisterLocationDropdowns() {
|
||||||
|
initRegisterLocationDropdowns().catch(function (err) {
|
||||||
|
console.error('Register location dropdowns failed to initialize', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', bootRegisterLocationDropdowns);
|
||||||
|
} else {
|
||||||
|
bootRegisterLocationDropdowns();
|
||||||
|
}
|
||||||
|
})();
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -70,12 +70,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -88,55 +83,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -236,6 +184,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -89,55 +84,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -242,6 +190,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
const FLATPICKR_CUSTOM_YEAR_SELECT = 'flatpickr-custom-year-select';
|
const FLATPICKR_CUSTOM_YEAR_SELECT = 'flatpickr-custom-year-select';
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -89,55 +84,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -242,6 +190,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -89,55 +84,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -242,6 +190,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -89,55 +84,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -258,6 +206,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -185,9 +185,17 @@
|
||||||
$('#field-header_params').html('')
|
$('#field-header_params').html('')
|
||||||
$('#field-body_params').html('')
|
$('#field-body_params').html('')
|
||||||
$('#field-button_params').html('')
|
$('#field-button_params').html('')
|
||||||
|
$('#field-button_cta').html('')
|
||||||
console.log(template_data)
|
console.log(template_data)
|
||||||
if(template_data) {
|
if(template_data) {
|
||||||
$('#field-template_body').html(template_data.template_body)
|
$('#field-template_body').html(template_data.template_body)
|
||||||
|
if(template_data.button_url || template_data.button_label) {
|
||||||
|
$('#field-button_cta').html(
|
||||||
|
'<div class="col-12 mb-2"><strong>Email button</strong></div>'
|
||||||
|
+ '<div class="col-12 mb-2 text-muted small">URL: ' + (template_data.button_url || '-') + '</div>'
|
||||||
|
+ '<div class="col-12 mb-2 text-muted small">Label: ' + (template_data.button_label || 'Click here') + '</div>'
|
||||||
|
)
|
||||||
|
}
|
||||||
if(template_data.header_params != null)
|
if(template_data.header_params != null)
|
||||||
for(let p of template_data.header_params.split('||') ) {
|
for(let p of template_data.header_params.split('||') ) {
|
||||||
$('#field-header_params').append('<div class="col-6 mb-2">'+p+'</div><div class="col-6 mb-2"><input type="text" name="header_params[]" class="form-control" value="'+p+'" required> </div>')
|
$('#field-header_params').append('<div class="col-6 mb-2">'+p+'</div><div class="col-6 mb-2"><input type="text" name="header_params[]" class="form-control" value="'+p+'" required> </div>')
|
||||||
|
|
|
||||||
|
|
@ -176,9 +176,17 @@
|
||||||
$('#field-header_params').html('')
|
$('#field-header_params').html('')
|
||||||
$('#field-body_params').html('')
|
$('#field-body_params').html('')
|
||||||
$('#field-button_params').html('')
|
$('#field-button_params').html('')
|
||||||
|
$('#field-button_cta').html('')
|
||||||
console.log(template_data)
|
console.log(template_data)
|
||||||
if(template_data) {
|
if(template_data) {
|
||||||
$('#field-template_body').html(template_data.template_body)
|
$('#field-template_body').html(template_data.template_body)
|
||||||
|
if(template_data.button_url || template_data.button_label) {
|
||||||
|
$('#field-button_cta').html(
|
||||||
|
'<div class="col-12 mb-2"><strong>Email button</strong></div>'
|
||||||
|
+ '<div class="col-12 mb-2 text-muted small">URL: ' + (template_data.button_url || '-') + '</div>'
|
||||||
|
+ '<div class="col-12 mb-2 text-muted small">Label: ' + (template_data.button_label || 'Click here') + '</div>'
|
||||||
|
)
|
||||||
|
}
|
||||||
if(template_data.header_params != null)
|
if(template_data.header_params != null)
|
||||||
for(let p of template_data.header_params.split('||') ) {
|
for(let p of template_data.header_params.split('||') ) {
|
||||||
$('#field-header_params').append('<div class="col-6 mb-2">'+p+'</div><div class="col-6 mb-2"><input type="text" name="header_params[]" class="form-control" value="'+p+'" required> </div>')
|
$('#field-header_params').append('<div class="col-6 mb-2">'+p+'</div><div class="col-6 mb-2"><input type="text" name="header_params[]" class="form-control" value="'+p+'" required> </div>')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
@php
|
||||||
|
$showRequiredAsterisk = $showRequiredAsterisk ?? false;
|
||||||
|
$defaultCountry = $defaultCountry ?? 'India';
|
||||||
|
$cityField = $cityField ?? 'city';
|
||||||
|
$cityLabel = $cityLabel ?? 'City';
|
||||||
|
$persistCountryState = $persistCountryState ?? true;
|
||||||
|
$requiredMark = $showRequiredAsterisk ? ' <span class="text-danger">*</span>' : '';
|
||||||
|
$countryName = $persistCountryState ? 'country' : '';
|
||||||
|
$stateName = $persistCountryState ? 'state' : '';
|
||||||
|
@endphp
|
||||||
|
<div class="register-location-fields" data-city-field="{{ $cityField }}" data-default-country="{{ $defaultCountry }}">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="country" class="col-4 col-xl-3 col-form-label">Country{!! $requiredMark !!}</label>
|
||||||
|
<div class="col-8 col-xl-9">
|
||||||
|
<select @if($countryName) name="{{ $countryName }}" @endif id="country" class="form-control register-location-country" required>
|
||||||
|
<option value="">-- Select Country --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="state" class="col-4 col-xl-3 col-form-label">State{!! $requiredMark !!}</label>
|
||||||
|
<div class="col-8 col-xl-9">
|
||||||
|
<select @if($stateName) name="{{ $stateName }}" @endif id="state" class="form-control register-location-state" required disabled>
|
||||||
|
<option value="">-- Select State --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3 register-location-city-row">
|
||||||
|
<label for="city" class="col-4 col-xl-3 col-form-label">{{ $cityLabel }}{!! $requiredMark !!}</label>
|
||||||
|
<div class="col-8 col-xl-9">
|
||||||
|
<select id="city" class="form-control register-location-city-select" required disabled style="display: none;">
|
||||||
|
<option value="">-- Select City --</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" class="form-control register-location-city-input" id="city_text" name="{{ $cityField }}" placeholder="Enter city" required disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
@php
|
||||||
|
$registerLocationCountries = [];
|
||||||
|
foreach ([
|
||||||
|
resource_path('data/countriesAndStates.json'),
|
||||||
|
public_path('assets/data/countriesAndStates.json'),
|
||||||
|
storage_path('app/geo/countriesAndStates.json'),
|
||||||
|
public_path('data/countriesAndStates.json'),
|
||||||
|
] as $countriesPath) {
|
||||||
|
if (is_readable($countriesPath)) {
|
||||||
|
$decoded = json_decode(file_get_contents($countriesPath), true);
|
||||||
|
if (is_array($decoded) && count($decoded) > 0 && array_keys($decoded) !== range(0, count($decoded) - 1)) {
|
||||||
|
$registerLocationCountries = $decoded;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
<script src="{{ asset('assets/js/register-location-countries-data.js') }}"></script>
|
||||||
|
@if(count($registerLocationCountries) > 0)
|
||||||
|
<script>
|
||||||
|
window.REGISTER_LOCATION_COUNTRIES = @json($registerLocationCountries);
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
|
<script>
|
||||||
|
window.REGISTER_LOCATION_CITIES_URL = @json(url('/geo/cities'));
|
||||||
|
</script>
|
||||||
|
<script src="{{ asset('assets/js/register-location-dropdowns.js') }}"></script>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
@if(($communication_mode ?? 'whatsapp') !== 'email')
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="phone" class="col-4 col-xl-3 col-form-label">
|
||||||
|
WhatsApp Mobile Number
|
||||||
|
@if(!empty($showRequiredStar))
|
||||||
|
<span class="text-danger">*</span>
|
||||||
|
@endif
|
||||||
|
</label>
|
||||||
|
<div class="col-8 col-xl-9">
|
||||||
|
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="{{ $phonePattern ?? '[6-9]{1}[0-9]{9}' }}" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
@ -68,15 +68,12 @@
|
||||||
required>
|
required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields', [
|
||||||
<label for="city_town" class="col-4 col-xl-3 col-form-label"><span
|
'showRequiredAsterisk' => true,
|
||||||
class="text-danger">*</span> City / Town </label>
|
'cityField' => 'city_town',
|
||||||
<div class="col-8 col-xl-9">
|
'cityLabel' => 'City / Town',
|
||||||
<input type="city_town" class="form-control" id="city_town" name="city_town"
|
'persistCountryState' => false,
|
||||||
value="{{ isset($userdata->city_town) ? $userdata->city_town : '' }}"
|
])
|
||||||
required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
|
||||||
<label for="qualification" class="col-4 col-xl-3 col-form-label"><span
|
<label for="qualification" class="col-4 col-xl-3 col-form-label"><span
|
||||||
|
|
@ -493,6 +490,7 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
<script src="{{ asset('assets/libs/flatpickr/flatpickr.min.js') }}"></script>
|
<script src="{{ asset('assets/libs/flatpickr/flatpickr.min.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -88,55 +83,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -247,6 +195,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-whatsapp-field')
|
||||||
<label for="phone" class="col-4 col-xl-3 col-form-label">WhatsApp Mobile Number</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="phone" name="phone" title="Enter valid 10-digit Mobile Number" pattern="[6-9]{1}[0-9]{9}" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
<label for="alternate_phone" class="col-4 col-xl-3 col-form-label">Alternate Contact Number</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -89,55 +84,8 @@
|
||||||
<input type="email" class="form-control" id="email" name="email" required>
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
@include('partials.register-location-fields')
|
||||||
<label for="city" class="col-4 col-xl-3 col-form-label">City</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<input type="text" class="form-control" id="city" name="city" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for="state" class="col-4 col-xl-3 col-form-label">State</label>
|
|
||||||
<div class="col-8 col-xl-9">
|
|
||||||
<select name="state" id="state" class="form-control" required>
|
|
||||||
<option value="Andhra Pradesh">Andhra Pradesh</option>
|
|
||||||
<option value="Andaman and Nicobar Islands">Andaman and Nicobar Islands</option>
|
|
||||||
<option value="Arunachal Pradesh">Arunachal Pradesh</option>
|
|
||||||
<option value="Assam">Assam</option>
|
|
||||||
<option value="Bihar">Bihar</option>
|
|
||||||
<option value="Chandigarh">Chandigarh</option>
|
|
||||||
<option value="Chhattisgarh">Chhattisgarh</option>
|
|
||||||
<option value="Dadar and Nagar Haveli">Dadar and Nagar Haveli</option>
|
|
||||||
<option value="Daman and Diu">Daman and Diu</option>
|
|
||||||
<option value="Delhi">Delhi</option>
|
|
||||||
<option value="Lakshadweep">Lakshadweep</option>
|
|
||||||
<option value="Puducherry">Puducherry</option>
|
|
||||||
<option value="Goa">Goa</option>
|
|
||||||
<option value="Gujarat">Gujarat</option>
|
|
||||||
<option value="Haryana">Haryana</option>
|
|
||||||
<option value="Himachal Pradesh">Himachal Pradesh</option>
|
|
||||||
<option value="Jammu and Kashmir">Jammu and Kashmir</option>
|
|
||||||
<option value="Jharkhand">Jharkhand</option>
|
|
||||||
<option value="Karnataka">Karnataka</option>
|
|
||||||
<option value="Kerala">Kerala</option>
|
|
||||||
<option value="Madhya Pradesh">Madhya Pradesh</option>
|
|
||||||
<option value="Maharashtra">Maharashtra</option>
|
|
||||||
<option value="Manipur">Manipur</option>
|
|
||||||
<option value="Meghalaya">Meghalaya</option>
|
|
||||||
<option value="Mizoram">Mizoram</option>
|
|
||||||
<option value="Nagaland">Nagaland</option>
|
|
||||||
<option value="Odisha">Odisha</option>
|
|
||||||
<option value="Punjab">Punjab</option>
|
|
||||||
<option value="Rajasthan">Rajasthan</option>
|
|
||||||
<option value="Sikkim">Sikkim</option>
|
|
||||||
<option value="Tamil Nadu">Tamil Nadu</option>
|
|
||||||
<option value="Telangana">Telangana</option>
|
|
||||||
<option value="Tripura">Tripura</option>
|
|
||||||
<option value="Uttar Pradesh">Uttar Pradesh</option>
|
|
||||||
<option value="Uttarakhand">Uttarakhand</option>
|
|
||||||
<option value="West Bengal">West Bengal</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
<label for="highlight_qualification" class="col-4 col-xl-3 col-form-label">Highest Qualification</label>
|
||||||
<div class="col-8 col-xl-9">
|
<div class="col-8 col-xl-9">
|
||||||
|
|
@ -254,6 +202,8 @@
|
||||||
<!-- end page -->
|
<!-- end page -->
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
@include('partials.register-location-scripts')
|
||||||
|
|
||||||
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
<script src="{{asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('.datepicker').flatpickr();
|
$('.datepicker').flatpickr();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\RegisterLocationController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
|
@ -14,6 +15,9 @@ use Illuminate\Support\Facades\Route;
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Route::get('/geo/countries', [RegisterLocationController::class, 'countries']);
|
||||||
|
Route::get('/geo/cities', [RegisterLocationController::class, 'cities']);
|
||||||
|
|
||||||
Route::middleware('auth:api')->get('/user', function (Request $request) {
|
Route::middleware('auth:api')->get('/user', function (Request $request) {
|
||||||
return $request->user();
|
return $request->user();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use App\Http\Controllers\FacultyController;
|
||||||
use App\Http\Controllers\SessionController;
|
use App\Http\Controllers\SessionController;
|
||||||
use App\Http\Controllers\SessionagendaController;
|
use App\Http\Controllers\SessionagendaController;
|
||||||
use App\Http\Controllers\MicrositeController;
|
use App\Http\Controllers\MicrositeController;
|
||||||
|
use App\Http\Controllers\RegisterLocationController;
|
||||||
use App\Http\Controllers\SessionagendalibController;
|
use App\Http\Controllers\SessionagendalibController;
|
||||||
|
|
||||||
use App\Http\Controllers\MsgtemplateController;
|
use App\Http\Controllers\MsgtemplateController;
|
||||||
|
|
@ -70,6 +71,10 @@ use App\Http\Controllers\CommentController;
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Public geo data for registration forms (must stay above catch-all routes).
|
||||||
|
Route::get('/geo/countries', [RegisterLocationController::class, 'countries']);
|
||||||
|
Route::get('/geo/cities', [RegisterLocationController::class, 'cities']);
|
||||||
|
|
||||||
Route::get('/', [DashboardController::class, 'index'])->name('home');
|
Route::get('/', [DashboardController::class, 'index'])->name('home');
|
||||||
Route::get('/index', [DashboardController::class, 'index'])->name('home');
|
Route::get('/index', [DashboardController::class, 'index'])->name('home');
|
||||||
Route::get('/login', [LoginController::class, 'index'])->name('login');
|
Route::get('/login', [LoginController::class, 'index'])->name('login');
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Achievements | CREST';
|
|
||||||
$metaDescription = 'See CREST milestones: lives impacted, doctors trained, studies completed, and partnerships across India.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Achievements</p>
|
|
||||||
<h2 class="fw-bold mb-4">Milestones powered by data and collaboration</h2>
|
|
||||||
<div class="row row-cols-2 row-cols-md-4 g-3">
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 rounded-4">
|
|
||||||
<div class="fw-bold">1Mn+</div>
|
|
||||||
<div class="text-muted small">Lives touched</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 rounded-4">
|
|
||||||
<div class="fw-bold">5k+</div>
|
|
||||||
<div class="text-muted small">Doctors trained</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 rounded-4">
|
|
||||||
<div class="fw-bold">500+</div>
|
|
||||||
<div class="text-muted small">Medical schools</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 rounded-4">
|
|
||||||
<div class="fw-bold">300+</div>
|
|
||||||
<div class="text-muted small">Research studies</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="text-muted mt-3">These numbers reflect multi-year partnerships, field programs, and training cohorts across India.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
$success = '';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$email = trim($_POST['email'] ?? '');
|
|
||||||
$password = trim($_POST['password'] ?? '');
|
|
||||||
|
|
||||||
if ($email === '' || $password === '') {
|
|
||||||
$error = 'Email and password are required.';
|
|
||||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$error = 'Please enter a valid email.';
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
create_admin($email, $password);
|
|
||||||
$success = 'Admin added successfully.';
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$error = 'Could not add admin (maybe email already exists).';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$pdo = db();
|
|
||||||
$admins = $pdo->query("SELECT email, created_at FROM admins ORDER BY created_at DESC");
|
|
||||||
$admins = is_array($admins) ? $admins : [];
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Admins';
|
|
||||||
$currentPage = '';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
form input { width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid #d8e7e0; font-size: 15px; margin-top: 8px; }
|
|
||||||
form label { font-weight: 700; color: #0f4333; display: block; margin-top: 12px; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Admins</p>
|
|
||||||
<h2>Manage administrator accounts</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($success): ?>
|
|
||||||
<div class="alert good"><?php echo htmlspecialchars($success); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert bad"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Add Admin</h3>
|
|
||||||
<form method="post">
|
|
||||||
<label for="email">Email</label>
|
|
||||||
<input type="email" id="email" name="email" required placeholder="admin@example.com">
|
|
||||||
|
|
||||||
<label for="password">Password</label>
|
|
||||||
<input type="password" id="password" name="password" required placeholder="Choose a secure password">
|
|
||||||
|
|
||||||
<div style="margin-top:16px;">
|
|
||||||
<button type="submit" class="btn primary">Add admin</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" style="margin-top:20px;">
|
|
||||||
<h3>Existing Admins</h3>
|
|
||||||
<?php if (empty($admins)): ?>
|
|
||||||
<p class="body muted">No admins found.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>Created</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($admins as $admin): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo htmlspecialchars($admin['email']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($admin['created_at']); ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$contacts = get_contacts();
|
|
||||||
$isExport = isset($_GET['export']);
|
|
||||||
if ($isExport) {
|
|
||||||
header('Content-Type: text/csv');
|
|
||||||
header('Content-Disposition: attachment; filename="contacts.csv"');
|
|
||||||
$out = fopen('php://output', 'w');
|
|
||||||
fputcsv($out, ['Name', 'Email', 'Organization', 'Message', 'Received']);
|
|
||||||
foreach ($contacts as $c) {
|
|
||||||
fputcsv($out, [
|
|
||||||
$c['name'] ?? '',
|
|
||||||
$c['email'] ?? '',
|
|
||||||
$c['organization'] ?? '',
|
|
||||||
$c['message'] ?? '',
|
|
||||||
$c['created_at'] ?? '',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
fclose($out);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Contact Entries';
|
|
||||||
$currentPage = 'contact';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; vertical-align: top; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Contact Entries</p>
|
|
||||||
<h2>Messages from the contact form</h2>
|
|
||||||
</div>
|
|
||||||
<div class="btn ghost" style="pointer-events:none;">Read-only</div>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin:10px 0;">
|
|
||||||
<a class="btn outline small" href="?export=1">Export CSV</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (empty($contacts)): ?>
|
|
||||||
<p class="body muted">No messages yet.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>Organization</th>
|
|
||||||
<th>Message</th>
|
|
||||||
<th>Received</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($contacts as $contact): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo htmlspecialchars($contact['name']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($contact['email']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($contact['organization'] ?? ''); ?></td>
|
|
||||||
<td><?php echo nl2br(htmlspecialchars($contact['message'])); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($contact['created_at']); ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$email = trim($_POST['email'] ?? '');
|
|
||||||
$password = trim($_POST['password'] ?? '');
|
|
||||||
if ($email === '' || $password === '') {
|
|
||||||
$error = 'Email and password are required.';
|
|
||||||
} elseif (attempt_login($email, $password)) {
|
|
||||||
header('Location: /admin/resources.php');
|
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
$error = 'Invalid credentials.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$pageTitle = 'Admin Login';
|
|
||||||
$currentPage = '';
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="section">
|
|
||||||
<div class="container" style="max-width:480px;">
|
|
||||||
<div class="card">
|
|
||||||
<h2>Admin Login</h2>
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert bad" style="margin-top:12px;"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<form method="post" style="display:grid; gap:12px; margin-top:12px;">
|
|
||||||
<div>
|
|
||||||
<label for="email">Email</label>
|
|
||||||
<input type="email" id="email" name="email" required placeholder="admin@example.com">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="password">Password</label>
|
|
||||||
<input type="password" id="password" name="password" required placeholder="••••••••">
|
|
||||||
</div>
|
|
||||||
<button class="btn primary" type="submit">Login</button>
|
|
||||||
<p class="body muted" style="font-size:13px;">Default admin: ravi@stratawiz.com / qwerty</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
logout();
|
|
||||||
header('Location: /admin/login.php');
|
|
||||||
exit;
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$subs = get_subscriptions();
|
|
||||||
$isExport = isset($_GET['export']);
|
|
||||||
if ($isExport) {
|
|
||||||
header('Content-Type: text/csv');
|
|
||||||
header('Content-Disposition: attachment; filename="newsletter.csv"');
|
|
||||||
$out = fopen('php://output', 'w');
|
|
||||||
fputcsv($out, ['First Name', 'Email', 'Subscribed']);
|
|
||||||
foreach ($subs as $s) {
|
|
||||||
fputcsv($out, [
|
|
||||||
$s['first_name'] ?? '',
|
|
||||||
$s['email'] ?? '',
|
|
||||||
$s['created_at'] ?? '',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
fclose($out);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Newsletter Subscribers';
|
|
||||||
$currentPage = 'news';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Newsletter</p>
|
|
||||||
<h2>Subscription list</h2>
|
|
||||||
</div>
|
|
||||||
<div class="btn ghost" style="pointer-events:none;">Read-only</div>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin:10px 0;">
|
|
||||||
<a class="btn outline small" href="?export=1">Export CSV</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (empty($subs)): ?>
|
|
||||||
<p class="body muted">No subscribers yet.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>First Name</th>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>Subscribed</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($subs as $sub): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo htmlspecialchars($sub['first_name'] ?? ''); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($sub['email']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($sub['created_at']); ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
$uploadDir = __DIR__ . '/../uploads/';
|
|
||||||
if (!is_dir($uploadDir)) {
|
|
||||||
mkdir($uploadDir, 0775, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$title = trim($_POST['title'] ?? '');
|
|
||||||
$summary = trim($_POST['summary'] ?? '');
|
|
||||||
$label = trim($_POST['label'] ?? '');
|
|
||||||
$video_url = trim($_POST['video_url'] ?? '');
|
|
||||||
$file_path = null;
|
|
||||||
|
|
||||||
if (isset($_FILES['resource_file']) && $_FILES['resource_file']['error'] !== UPLOAD_ERR_NO_FILE) {
|
|
||||||
$file = $_FILES['resource_file'];
|
|
||||||
if ($file['error'] === UPLOAD_ERR_OK) {
|
|
||||||
$allowed = [
|
|
||||||
'application/pdf',
|
|
||||||
'application/msword',
|
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
||||||
];
|
|
||||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
||||||
$mime = finfo_file($finfo, $file['tmp_name']);
|
|
||||||
finfo_close($finfo);
|
|
||||||
if (!in_array($mime, $allowed, true)) {
|
|
||||||
$error = 'Only PDF or Word documents are allowed.';
|
|
||||||
} else {
|
|
||||||
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
|
|
||||||
$newName = 'resource_' . time() . '_' . mt_rand(1000, 9999) . '.' . $ext;
|
|
||||||
$dest = $uploadDir . $newName;
|
|
||||||
if (move_uploaded_file($file['tmp_name'], $dest)) {
|
|
||||||
$file_path = 'uploads/' . $newName;
|
|
||||||
} else {
|
|
||||||
$error = 'File upload failed.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$error = 'File upload failed.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($title === '' || $summary === '' || $label === '') {
|
|
||||||
$error = 'All fields are required.';
|
|
||||||
} elseif (!$error) {
|
|
||||||
add_resource(compact('title', 'summary', 'label', 'file_path', 'video_url'));
|
|
||||||
header('Location: resources.php?success=1');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['delete'])) {
|
|
||||||
$id = (int) $_GET['delete'];
|
|
||||||
delete_resource($id);
|
|
||||||
header('Location: resources.php?deleted=1');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$resources = get_resources();
|
|
||||||
echo '<pre>'; print_r($resources); exit;
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Manage Resources';
|
|
||||||
$currentPage = 'resources';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
.badge { display: inline-block; padding: 6px 10px; border-radius: 10px; background: #e8f5ef; color: #0f4333; font-weight: 700; font-size: 13px; }
|
|
||||||
.inline-btn { color: #b9342f; font-weight: 700; }
|
|
||||||
form textarea { min-height: 120px; resize: vertical; }
|
|
||||||
form input, form textarea { width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid #d8e7e0; font-size: 15px; margin-top: 8px; }
|
|
||||||
form input[type=file] { padding: 8px; }
|
|
||||||
form label { font-weight: 700; color: #0f4333; display: block; margin-top: 12px; }
|
|
||||||
.topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Resources</p>
|
|
||||||
<h2>Resources for Learning and Research</h2>
|
|
||||||
<p class="body">Add short descriptions and labels; they will appear instantly on the public site.</p>
|
|
||||||
</div>
|
|
||||||
<button class="btn primary" onclick="document.getElementById('resource-form').scrollIntoView({behavior: 'smooth'})">Add new</button>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (isset($_GET['success'])): ?>
|
|
||||||
<div class="alert good">Resource added successfully.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (isset($_GET['deleted'])): ?>
|
|
||||||
<div class="alert good">Resource deleted.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert bad"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if (empty($resources)): ?>
|
|
||||||
<p class="body muted">No resources yet. Add your first entry below.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Summary</th>
|
|
||||||
<th>Label</th>
|
|
||||||
<th>Asset</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($resources as $index => $resource): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($resource)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$title = htmlspecialchars((string)($resource['title'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$summary = htmlspecialchars((string)($resource['summary'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$label = htmlspecialchars((string)($resource['label'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$filePath = (string)($resource['file_path'] ?? '');
|
|
||||||
$videoUrl = (string)($resource['video_url'] ?? '');
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $title; ?></td>
|
|
||||||
<td><?php echo $summary; ?></td>
|
|
||||||
<td><span class="badge"><?php echo $label; ?></span></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($filePath): ?>
|
|
||||||
<a class="text-link" href="/<?php echo htmlspecialchars(ltrim($filePath, '/'), ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Download</a>
|
|
||||||
<?php elseif ($videoUrl): ?>
|
|
||||||
<a class="text-link" href="<?php echo htmlspecialchars($videoUrl, ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Watch</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="body muted">None</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td><a class="inline-btn" href="?delete=<?php echo (int)$resource['id']; ?>" onclick="return confirm('Delete this resource?')">Delete</a></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<section id="resource-form" style="margin-top:32px;">
|
|
||||||
<div class="card">
|
|
||||||
<h3>Add Resource</h3>
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
<label for="title">Title</label>
|
|
||||||
<input type="text" id="title" name="title" required placeholder="Resource title">
|
|
||||||
|
|
||||||
<label for="summary">Summary</label>
|
|
||||||
<textarea id="summary" name="summary" required placeholder="Brief description"></textarea>
|
|
||||||
|
|
||||||
<label for="label">Label / Type</label>
|
|
||||||
<input type="text" id="label" name="label" required placeholder="e.g., Publication, Toolkit">
|
|
||||||
|
|
||||||
<label for="video_url">YouTube / Video URL (optional)</label>
|
|
||||||
<input type="url" id="video_url" name="video_url" placeholder="https://youtube.com/...">
|
|
||||||
|
|
||||||
<label for="resource_file">Upload PDF / Word (optional)</label>
|
|
||||||
<input type="file" id="resource_file" name="resource_file" accept=".pdf,.doc,.docx">
|
|
||||||
<p class="body muted" style="margin:6px 0 0;">Upload a file or provide a video URL (or both).</p>
|
|
||||||
|
|
||||||
<div style="margin-top:16px;">
|
|
||||||
<button type="submit" class="btn primary">Save resource</button>
|
|
||||||
<a class="text-link" href="../index.php" style="margin-left:10px;">View live site</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$title = trim($_POST['title'] ?? '');
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$tag = trim($_POST['tag'] ?? '');
|
|
||||||
$cta_label = trim($_POST['cta_label'] ?? 'Register');
|
|
||||||
$start_date = $_POST['start_date'] ?? null;
|
|
||||||
$editId = isset($_POST['id']) ? (int) $_POST['id'] : null;
|
|
||||||
|
|
||||||
if ($title === '' || $description === '') {
|
|
||||||
$error = 'Title and description are required.';
|
|
||||||
} else {
|
|
||||||
if ($editId) {
|
|
||||||
update_session($editId, compact('title', 'description', 'tag', 'cta_label', 'start_date'));
|
|
||||||
header('Location: sessions.php?updated=1');
|
|
||||||
} else {
|
|
||||||
add_session(compact('title', 'description', 'tag', 'cta_label', 'start_date'));
|
|
||||||
header('Location: sessions.php?success=1');
|
|
||||||
}
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['delete'])) {
|
|
||||||
$id = (int) $_GET['delete'];
|
|
||||||
delete_session($id);
|
|
||||||
header('Location: sessions.php?deleted=1');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sessions = get_sessions();
|
|
||||||
$editing = null;
|
|
||||||
if (isset($_GET['edit'])) {
|
|
||||||
$editId = (int) $_GET['edit'];
|
|
||||||
foreach ($sessions as $row) {
|
|
||||||
if ((int) $row['id'] === $editId) {
|
|
||||||
$editing = $row;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Manage Sessions';
|
|
||||||
$currentPage = 'news';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
.badge { display: inline-block; padding: 6px 10px; border-radius: 10px; background: #e8f5ef; color: #0f4333; font-weight: 700; font-size: 13px; }
|
|
||||||
.inline-btn { color: #b9342f; font-weight: 700; }
|
|
||||||
form textarea { min-height: 120px; resize: vertical; }
|
|
||||||
form input, form textarea { width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid #d8e7e0; font-size: 15px; margin-top: 8px; }
|
|
||||||
form label { font-weight: 700; color: #0f4333; display: block; margin-top: 12px; }
|
|
||||||
.topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Upcoming Sessions</p>
|
|
||||||
<h2>Manage events and workshops</h2>
|
|
||||||
<p class="body">Add session details to show on the public site.</p>
|
|
||||||
</div>
|
|
||||||
<button class="btn primary" onclick="document.getElementById('session-form').scrollIntoView({behavior:'smooth'})">Add new</button>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (isset($_GET['success'])): ?>
|
|
||||||
<div class="alert good">Session added successfully.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (isset($_GET['deleted'])): ?>
|
|
||||||
<div class="alert good">Session deleted.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (isset($_GET['updated'])): ?>
|
|
||||||
<div class="alert good">Session updated.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert bad"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if (empty($sessions)): ?>
|
|
||||||
<p class="body muted">No sessions yet. Add your first event below.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Tag</th>
|
|
||||||
<th>Start Date</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($sessions as $session): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo htmlspecialchars($session['title']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($session['description']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($session['tag']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($session['start_date'] ?? ''); ?></td>
|
|
||||||
<td>
|
|
||||||
<a class="inline-btn" href="?edit=<?php echo (int)$session['id']; ?>">Edit</a> |
|
|
||||||
<a class="inline-btn" href="?delete=<?php echo (int)$session['id']; ?>" onclick="return confirm('Delete this session?')">Delete</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<section id="session-form" style="margin-top:32px;">
|
|
||||||
<div class="card">
|
|
||||||
<h3><?php echo $editing ? 'Edit Session' : 'Add Session'; ?></h3>
|
|
||||||
<form method="post">
|
|
||||||
<?php if ($editing): ?>
|
|
||||||
<input type="hidden" name="id" value="<?php echo (int)$editing['id']; ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<label for="title">Title</label>
|
|
||||||
<input type="text" id="title" name="title" required placeholder="Session title" value="<?php echo htmlspecialchars($editing['title'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<label for="description">Description</label>
|
|
||||||
<textarea id="description" name="description" required placeholder="Brief description"><?php echo htmlspecialchars($editing['description'] ?? '', ENT_QUOTES, 'UTF-8'); ?></textarea>
|
|
||||||
|
|
||||||
<label for="tag">Tag</label>
|
|
||||||
<input type="text" id="tag" name="tag" placeholder="e.g., New, Upcoming" value="<?php echo htmlspecialchars($editing['tag'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<label for="cta_label">CTA Label</label>
|
|
||||||
<input type="text" id="cta_label" name="cta_label" placeholder="Register" value="<?php echo htmlspecialchars($editing['cta_label'] ?? 'Register', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<label for="start_date">Start Date</label>
|
|
||||||
<input type="date" id="start_date" name="start_date" value="<?php echo htmlspecialchars($editing['start_date'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<div style="margin-top:16px;">
|
|
||||||
<button type="submit" class="btn primary"><?php echo $editing ? 'Update session' : 'Save session'; ?></button>
|
|
||||||
<a class="text-link" href="/news-events.php" style="margin-left:10px;">View live</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/../includes/auth.php';
|
|
||||||
require_login();
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
$uploadDir = __DIR__ . '/../uploads/';
|
|
||||||
if (!is_dir($uploadDir)) {
|
|
||||||
mkdir($uploadDir, 0775, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$title = trim($_POST['title'] ?? '');
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
|
||||||
$status = trim($_POST['status'] ?? 'active');
|
|
||||||
$video_url = trim($_POST['video_url'] ?? '');
|
|
||||||
$file_path = null;
|
|
||||||
$editId = isset($_POST['id']) ? (int) $_POST['id'] : null;
|
|
||||||
|
|
||||||
if (isset($_FILES['tutorial_file']) && $_FILES['tutorial_file']['error'] !== UPLOAD_ERR_NO_FILE) {
|
|
||||||
$file = $_FILES['tutorial_file'];
|
|
||||||
if ($file['error'] === UPLOAD_ERR_OK) {
|
|
||||||
$allowed = [
|
|
||||||
'application/pdf',
|
|
||||||
'application/msword',
|
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
||||||
];
|
|
||||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
||||||
$mime = finfo_file($finfo, $file['tmp_name']);
|
|
||||||
finfo_close($finfo);
|
|
||||||
if (!in_array($mime, $allowed, true)) {
|
|
||||||
$error = 'Only PDF or Word documents are allowed.';
|
|
||||||
} else {
|
|
||||||
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
|
|
||||||
$newName = 'tutorial_' . time() . '_' . mt_rand(1000, 9999) . '.' . $ext;
|
|
||||||
$dest = $uploadDir . $newName;
|
|
||||||
if (move_uploaded_file($file['tmp_name'], $dest)) {
|
|
||||||
$file_path = 'uploads/' . $newName;
|
|
||||||
} else {
|
|
||||||
$error = 'File upload failed.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$error = 'File upload failed.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($title === '' || $description === '') {
|
|
||||||
$error = 'Title and description are required.';
|
|
||||||
} elseif (!$error) {
|
|
||||||
$payload = compact('title', 'description', 'status', 'file_path', 'video_url');
|
|
||||||
if ($editId) {
|
|
||||||
update_tutorial($editId, $payload);
|
|
||||||
header('Location: tutorials.php?updated=1');
|
|
||||||
} else {
|
|
||||||
add_tutorial($payload);
|
|
||||||
header('Location: tutorials.php?success=1');
|
|
||||||
}
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET['delete'])) {
|
|
||||||
$id = (int) $_GET['delete'];
|
|
||||||
delete_tutorial($id);
|
|
||||||
header('Location: tutorials.php?deleted=1');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tutorials = get_tutorials(false);
|
|
||||||
$editing = null;
|
|
||||||
if (isset($_GET['edit'])) {
|
|
||||||
$editId = (int) $_GET['edit'];
|
|
||||||
foreach ($tutorials as $row) {
|
|
||||||
if ((int) $row['id'] === $editId) {
|
|
||||||
$editing = $row;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$assetPathPrefix = '../';
|
|
||||||
$pageTitle = 'Manage Tutorials';
|
|
||||||
$currentPage = 'tutorials';
|
|
||||||
$extraStyles = <<<CSS
|
|
||||||
<style>
|
|
||||||
body { background: #f6faf7; }
|
|
||||||
.admin-shell { padding: 40px 0; }
|
|
||||||
.table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
||||||
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid #e1ede7; }
|
|
||||||
.table th { color: #0f4333; }
|
|
||||||
.badge { display: inline-block; padding: 6px 10px; border-radius: 10px; background: #e8f5ef; color: #0f4333; font-weight: 700; font-size: 13px; }
|
|
||||||
.inline-btn { color: #b9342f; font-weight: 700; }
|
|
||||||
form textarea { min-height: 120px; resize: vertical; }
|
|
||||||
form input, form textarea, form select { width: 100%; padding: 12px 14px; border-radius: 10px; border: 1px solid #d8e7e0; font-size: 15px; margin-top: 8px; }
|
|
||||||
form input[type=file] { padding: 8px; }
|
|
||||||
form label { font-weight: 700; color: #0f4333; display: block; margin-top: 12px; }
|
|
||||||
.topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
||||||
</style>
|
|
||||||
CSS;
|
|
||||||
|
|
||||||
require __DIR__ . '/../includes/admin-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main class="admin-shell">
|
|
||||||
<div class="container">
|
|
||||||
<div class="topbar">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Tutorials</p>
|
|
||||||
<h2>Manage tutorials</h2>
|
|
||||||
<p class="body">Add training tutorials with downloads or video links.</p>
|
|
||||||
</div>
|
|
||||||
<button class="btn primary" onclick="document.getElementById('tutorial-form').scrollIntoView({behavior: 'smooth'})">Add new</button>
|
|
||||||
</div>
|
|
||||||
<div class="admin-nav">
|
|
||||||
<a href="/admin/tutorials.php">Tutorials</a>
|
|
||||||
<a href="/admin/sessions.php">Upcoming Sessions</a>
|
|
||||||
<a href="/admin/contacts.php">Contact Entries</a>
|
|
||||||
<a href="/admin/newsletter.php">Newsletter</a>
|
|
||||||
<a href="/admin/admins.php">Admins</a>
|
|
||||||
<a href="/admin/logout.php">Logout (<?php echo htmlspecialchars(current_admin_email() ?? ''); ?>)</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (isset($_GET['success'])): ?>
|
|
||||||
<div class="alert good">Tutorial added successfully.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (isset($_GET['deleted'])): ?>
|
|
||||||
<div class="alert good">Tutorial deleted.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (isset($_GET['updated'])): ?>
|
|
||||||
<div class="alert good">Tutorial updated successfully.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert bad"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if (empty($tutorials)): ?>
|
|
||||||
<p class="body muted">No tutorials yet. Add your first entry below.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Asset</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($tutorials as $tutorial): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($tutorial)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$title = htmlspecialchars((string)($tutorial['title'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$description = htmlspecialchars((string)($tutorial['description'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$status = htmlspecialchars((string)($tutorial['status'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$filePath = (string)($tutorial['file_path'] ?? '');
|
|
||||||
$videoUrl = (string)($tutorial['video_url'] ?? '');
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $title; ?></td>
|
|
||||||
<td><?php echo $description; ?></td>
|
|
||||||
<td><span class="badge"><?php echo $status; ?></span></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($filePath): ?>
|
|
||||||
<a class="text-link" href="/<?php echo htmlspecialchars(ltrim($filePath, '/'), ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Download</a>
|
|
||||||
<?php elseif ($videoUrl): ?>
|
|
||||||
<a class="text-link" href="<?php echo htmlspecialchars($videoUrl, ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Watch</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="body muted">None</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="inline-btn" href="?edit=<?php echo (int)$tutorial['id']; ?>">Edit</a> |
|
|
||||||
<a class="inline-btn" href="?delete=<?php echo (int)$tutorial['id']; ?>" onclick="return confirm('Delete this tutorial?')">Delete</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<section id="tutorial-form" style="margin-top:32px;">
|
|
||||||
<div class="card">
|
|
||||||
<h3><?php echo $editing ? 'Edit Tutorial' : 'Add Tutorial'; ?></h3>
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
<?php if ($editing): ?>
|
|
||||||
<input type="hidden" name="id" value="<?php echo (int)$editing['id']; ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<label for="title">Title</label>
|
|
||||||
<input type="text" id="title" name="title" required placeholder="Tutorial title" value="<?php echo htmlspecialchars($editing['title'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<label for="description">Description</label>
|
|
||||||
<textarea id="description" name="description" required placeholder="Brief description"><?php echo htmlspecialchars($editing['description'] ?? '', ENT_QUOTES, 'UTF-8'); ?></textarea>
|
|
||||||
|
|
||||||
<label for="status">Status</label>
|
|
||||||
<select id="status" name="status">
|
|
||||||
<option value="active" <?php echo (($editing['status'] ?? '') === 'active') ? 'selected' : ''; ?>>Active</option>
|
|
||||||
<option value="draft" <?php echo (($editing['status'] ?? '') === 'draft') ? 'selected' : ''; ?>>Draft</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label for="video_url">YouTube / Video URL (optional)</label>
|
|
||||||
<input type="url" id="video_url" name="video_url" placeholder="https://youtube.com/..." value="<?php echo htmlspecialchars($editing['video_url'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
|
|
||||||
<label for="tutorial_file">Upload PDF / Word (optional)</label>
|
|
||||||
<input type="file" id="tutorial_file" name="tutorial_file" accept=".pdf,.doc,.docx">
|
|
||||||
<?php if (!empty($editing['file_path'])): ?>
|
|
||||||
<p class="body muted">Current file: <a href="/<?php echo htmlspecialchars($editing['file_path'], ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Download</a></p>
|
|
||||||
<?php endif; ?>
|
|
||||||
<p class="body muted" style="margin:6px 0 0;">Upload a file or provide a video URL (or both).</p>
|
|
||||||
|
|
||||||
<div style="margin-top:16px;">
|
|
||||||
<button type="submit" class="btn primary"><?php echo $editing ? 'Update tutorial' : 'Save tutorial'; ?></button>
|
|
||||||
<a class="text-link" href="/index.php" style="margin-left:10px;">View live site</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/../includes/admin-footer.php'; ?>
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
body.admin-body {
|
|
||||||
background: #f6faf7;
|
|
||||||
font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
||||||
color: #0e1e18;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-header {
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-mark {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: linear-gradient(135deg, #2bc08a, #11704f);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-name {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-tagline {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell {
|
|
||||||
padding: 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-nav {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 10px 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-nav a {
|
|
||||||
background: #e6f3ed;
|
|
||||||
color: #0f4333;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border-radius: 16px;
|
|
||||||
border: 1px solid #e5efe9;
|
|
||||||
box-shadow: 0 10px 24px rgba(20, 72, 55, 0.08);
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.primary,
|
|
||||||
.btn.btn-primary {
|
|
||||||
background: #1d9c6d;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.outline,
|
|
||||||
.btn.btn-outline {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #d3e8e0;
|
|
||||||
color: #0f4333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.small {
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input,
|
|
||||||
form textarea,
|
|
||||||
form select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 14px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid #d8e7e0;
|
|
||||||
font-size: 15px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form textarea {
|
|
||||||
min-height: 120px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
form label {
|
|
||||||
font-weight: 700;
|
|
||||||
color: #0f4333;
|
|
||||||
display: block;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert.good {
|
|
||||||
background: #def3eb;
|
|
||||||
color: #0f4333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert.bad {
|
|
||||||
background: #ffebe8;
|
|
||||||
color: #9a2f23;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th,
|
|
||||||
.table td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-footer {
|
|
||||||
background: #f0f5f3;
|
|
||||||
border-top: 1px solid #e0ebe5;
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
.brand-mark {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
background: linear-gradient(135deg, #2bc08a, #11704f);
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section {
|
|
||||||
background: radial-gradient(140% 120% at 25% 20%, #e9f7f0 0%, #ffffff 60%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-tile {
|
|
||||||
background: #eef8f5;
|
|
||||||
border: 1px solid #def3eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-orb {
|
|
||||||
width: 220px;
|
|
||||||
height: 220px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: radial-gradient(circle at 30% 30%, #ffb7a6, #e06a6a 60%);
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
box-shadow: 0 18px 40px rgba(223, 121, 121, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-orb-inner {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
background: linear-gradient(180deg, #ffede7, #ffb9a6);
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-pill {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #e8f5ef;
|
|
||||||
color: #0f6f52;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder-img {
|
|
||||||
min-height: 200px;
|
|
||||||
background: linear-gradient(135deg, #c2e7d9, #9cd3c3);
|
|
||||||
}
|
|
||||||
.placeholder-img.tall { min-height: 260px; }
|
|
||||||
.placeholder-img.small { min-height: 140px; }
|
|
||||||
.placeholder-img.mailer {
|
|
||||||
min-height: 220px;
|
|
||||||
background: linear-gradient(135deg, #f6f6f6, #ffffff);
|
|
||||||
box-shadow: 0 12px 30px rgba(0,0,0,0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge.bg-success-subtle {
|
|
||||||
background: #e8f5ef !important;
|
|
||||||
color: #0f6f52 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a { color: #fff; }
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const form = document.querySelector('.newsletter-form');
|
|
||||||
if (form) {
|
|
||||||
form.addEventListener('submit', () => {
|
|
||||||
// Let the form submit normally; no client-side prevention so server can record subscriptions.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Contact | CREST';
|
|
||||||
$metaDescription = 'Contact CREST for research collaborations, training, media, or partnership inquiries in respiratory health.';
|
|
||||||
$contactMessage = '';
|
|
||||||
$contactError = '';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$email = trim($_POST['email'] ?? '');
|
|
||||||
$organization = trim($_POST['organization'] ?? '');
|
|
||||||
$message = trim($_POST['message'] ?? '');
|
|
||||||
|
|
||||||
if ($name === '' || $email === '' || $message === '') {
|
|
||||||
$contactError = 'Name, email, and message are required.';
|
|
||||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$contactError = 'Please enter a valid email.';
|
|
||||||
} else {
|
|
||||||
save_contact(compact('name', 'email', 'organization', 'message'));
|
|
||||||
$contactMessage = 'Thanks for reaching out. We will get back to you within 2 business days.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Contact</p>
|
|
||||||
<h2 class="fw-bold">We would love to hear from you</h2>
|
|
||||||
<p class="text-muted">Tell us about your research needs, training goals, or partnership ideas.</p>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li class="mb-2">• Program design consultations</li>
|
|
||||||
<li class="mb-2">• Training calendar requests</li>
|
|
||||||
<li class="mb-2">• Media and speaking inquiries</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card shadow-sm border-0 rounded-4 p-3">
|
|
||||||
<?php if ($contactMessage): ?>
|
|
||||||
<div class="alert alert-success"><?php echo htmlspecialchars($contactMessage); ?></div>
|
|
||||||
<?php elseif ($contactError): ?>
|
|
||||||
<div class="alert alert-danger"><?php echo htmlspecialchars($contactError); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<form class="row g-3" method="post">
|
|
||||||
<div class="col-12">
|
|
||||||
<input type="text" name="name" class="form-control rounded-3" placeholder="Full Name" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<input type="email" name="email" class="form-control rounded-3" placeholder="Email" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<input type="text" name="organization" class="form-control rounded-3" placeholder="Organization">
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<textarea name="message" class="form-control rounded-3" rows="4" placeholder="How can we help?" required></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 d-grid">
|
|
||||||
<button class="btn btn-success text-white rounded-3" type="submit">Send message</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"title": "Understanding Lung Function in Adult Populations",
|
|
||||||
"summary": "A concise explainer on spirometry metrics and how to interpret lung function assessments in community settings.",
|
|
||||||
"label": "Publication"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Lung Function in India: Overview",
|
|
||||||
"summary": "Key findings from multi-city research that maps prevalence of respiratory conditions across demographic groups.",
|
|
||||||
"label": "Research Brief"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Childhood Lung Function in India",
|
|
||||||
"summary": "Practical guidance for pediatric screening teams and frontline clinicians working with young patients.",
|
|
||||||
"label": "Toolkit"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Understanding Lung Function in Rural Populations",
|
|
||||||
"summary": "Field notes and methodologies for conducting door-to-door lung health studies with limited resources.",
|
|
||||||
"label": "Field Guide"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Our Expertise | CREST';
|
|
||||||
$metaDescription = 'Discover CREST services in research, training, and consulting for respiratory health programs.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5 bg-light">
|
|
||||||
<div class="container">
|
|
||||||
<div class="mb-4">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Our Expertise</p>
|
|
||||||
<h2 class="fw-bold">Our Services & Expertise</h2>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Research</h5>
|
|
||||||
<p class="text-muted small">Rigorous studies to map lung health trends and inform community programs.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Training</h5>
|
|
||||||
<p class="text-muted small">Workshops and mentoring that upskill clinicians and field teams.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Consulting</h5>
|
|
||||||
<p class="text-muted small">Advisory for programs to scale respiratory care safely and effectively.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
$pageTitle = 'Our Experts | Crest Lung Health';
|
|
||||||
$currentPage = 'experts';
|
|
||||||
require __DIR__ . '/includes/header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="section">
|
|
||||||
<div class="container two-col">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Our Experts</p>
|
|
||||||
<h1>Meet the team driving respiratory innovation</h1>
|
|
||||||
<p class="body">Clinical specialists, researchers, and educators collaborate to design programs that translate science into care. Each team member brings domain expertise across pulmonology, epidemiology, community health, and implementation.</p>
|
|
||||||
<ul class="checklist">
|
|
||||||
<li><span class="dot green"></span> 300+ specialists and faculty contributors</li>
|
|
||||||
<li><span class="dot green"></span> Cross-functional teams for research and training</li>
|
|
||||||
<li><span class="dot green"></span> Field experience across diverse geographies</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="photo-card">
|
|
||||||
<div class="photo tall"></div>
|
|
||||||
<div class="photo caption">Trusted by leading institutes</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<footer class="admin-footer">
|
|
||||||
<div class="container text-center small text-muted py-3">
|
|
||||||
©2025 Stratawiz.com
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="<?php echo $assetPathPrefix; ?>assets/js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
|
||||||
session_start();
|
|
||||||
}
|
|
||||||
$assetPathPrefix = $assetPathPrefix ?? '';
|
|
||||||
$pageTitle = $pageTitle ?? 'Admin';
|
|
||||||
$loggedEmail = $_SESSION['admin_email'] ?? null;
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="<?php echo $assetPathPrefix; ?>assets/css/admin.css">
|
|
||||||
<?php if (!empty($extraStyles)) { echo $extraStyles; } ?>
|
|
||||||
</head>
|
|
||||||
<body class="admin-body">
|
|
||||||
<header class="site-header shadow-sm">
|
|
||||||
<div class="container d-flex justify-content-between align-items-center py-3">
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
|
||||||
<div class="brand-mark text-white fw-bold d-flex align-items-center justify-content-center">C</div>
|
|
||||||
<div>
|
|
||||||
<div class="brand-name text-success fw-bold">CREST</div>
|
|
||||||
<div class="brand-tagline text-muted">Admin Panel</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fw-semibold text-muted">
|
|
||||||
<?php echo $loggedEmail ? htmlspecialchars($loggedEmail) : ''; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/data.php';
|
|
||||||
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
|
||||||
session_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
function require_login(): void
|
|
||||||
{
|
|
||||||
if (!is_logged_in()) {
|
|
||||||
$loginPath = 'login.php';
|
|
||||||
if (strpos($_SERVER['REQUEST_URI'] ?? '', '/admin/') === false) {
|
|
||||||
$loginPath = '/admin/login.php';
|
|
||||||
}
|
|
||||||
header('Location: ' . $loginPath);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_logged_in(): bool
|
|
||||||
{
|
|
||||||
return isset($_SESSION['admin_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function current_admin_email(): ?string
|
|
||||||
{
|
|
||||||
return $_SESSION['admin_email'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function attempt_login(string $email, string $password): bool
|
|
||||||
{
|
|
||||||
$admin = get_admin_by_email($email);
|
|
||||||
if (!$admin) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password_verify($password, $admin['password_hash'])) {
|
|
||||||
$_SESSION['admin_id'] = $admin['id'];
|
|
||||||
$_SESSION['admin_email'] = $admin['email'];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout(): void
|
|
||||||
{
|
|
||||||
$_SESSION = [];
|
|
||||||
session_destroy();
|
|
||||||
}
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once __DIR__ . '/db.php';
|
|
||||||
|
|
||||||
if (!function_exists('get_resources')) {
|
|
||||||
function get_resources(): array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$table = resource_table_name($pdo);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$rows = $pdo->query("SELECT * FROM resources_table ORDER BY id DESC");
|
|
||||||
return is_array($rows) ? $rows : [];
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
error_log('get_resources attempt failed: ' . $e->getMessage());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('add_resource')) {
|
|
||||||
function add_resource(array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$table = resource_table_name($pdo);
|
|
||||||
$pdo->insert($table, [
|
|
||||||
'title' => trim($payload['title'] ?? ''),
|
|
||||||
'summary' => trim($payload['summary'] ?? ''),
|
|
||||||
'label' => trim($payload['label'] ?? ''),
|
|
||||||
'file_path' => $payload['file_path'] ?? null,
|
|
||||||
'video_url' => trim($payload['video_url'] ?? ''),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('delete_resource')) {
|
|
||||||
function delete_resource(int $id): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$table = resource_table_name($pdo);
|
|
||||||
$filePath = null;
|
|
||||||
$row = $pdo->getOne("SELECT file_path FROM `$table` WHERE id = ?", [$id]);
|
|
||||||
if ($row && isset($row['file_path'])) {
|
|
||||||
$filePath = $row['file_path'];
|
|
||||||
}
|
|
||||||
$pdo->deleteById($table, $id);
|
|
||||||
if ($filePath && is_string($filePath)) {
|
|
||||||
$full = __DIR__ . '/../' . ltrim($filePath, '/');
|
|
||||||
if (is_file($full)) {
|
|
||||||
@unlink($full);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('get_sessions')) {
|
|
||||||
function get_sessions(): array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$rows = $pdo->query("
|
|
||||||
SELECT id, number, start_date, status, join_link, code, venue
|
|
||||||
FROM sessions
|
|
||||||
WHERE (status IN ('current','upcoming') OR start_date >= NOW())
|
|
||||||
AND (deleted_at IS NULL OR deleted_at = '0000-00-00 00:00:00')
|
|
||||||
ORDER BY start_date ASC
|
|
||||||
");
|
|
||||||
return is_array($rows) ? $rows : [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('add_session')) {
|
|
||||||
function add_session(array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->insert('sessions', [
|
|
||||||
'title' => trim($payload['title'] ?? ''),
|
|
||||||
'description' => trim($payload['description'] ?? ''),
|
|
||||||
'tag' => trim($payload['tag'] ?? ''),
|
|
||||||
'cta_label' => trim($payload['cta_label'] ?? 'Register'),
|
|
||||||
'start_date' => $payload['start_date'] ?? null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('update_session')) {
|
|
||||||
function update_session(int $id, array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->update('sessions', [
|
|
||||||
'title' => trim($payload['title'] ?? ''),
|
|
||||||
'description' => trim($payload['description'] ?? ''),
|
|
||||||
'tag' => trim($payload['tag'] ?? ''),
|
|
||||||
'cta_label' => trim($payload['cta_label'] ?? 'Register'),
|
|
||||||
'start_date' => $payload['start_date'] ?? null,
|
|
||||||
], "id = ?", [$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('delete_session')) {
|
|
||||||
function delete_session(int $id): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->deleteById('sessions', $id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('save_contact')) {
|
|
||||||
function save_contact(array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->insert('contacts', [
|
|
||||||
'name' => trim($payload['name'] ?? ''),
|
|
||||||
'email' => trim($payload['email'] ?? ''),
|
|
||||||
'organization' => trim($payload['organization'] ?? ''),
|
|
||||||
'message' => trim($payload['message'] ?? ''),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('get_contacts')) {
|
|
||||||
function get_contacts(): array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
return $pdo->query("SELECT id, name, email, organization, message, created_at FROM contacts ORDER BY id DESC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('get_tutorials')) {
|
|
||||||
function get_tutorials(bool $onlyActive = true): array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$sql = "SELECT id, title, description, status, file_path, video_url, created_at FROM tutorials";
|
|
||||||
$params = [];
|
|
||||||
if ($onlyActive) {
|
|
||||||
$sql .= " WHERE status = ?";
|
|
||||||
$params[] = 'active';
|
|
||||||
}
|
|
||||||
$sql .= " ORDER BY id DESC";
|
|
||||||
$rows = $pdo->query($sql, $params);
|
|
||||||
return is_array($rows) ? $rows : [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('add_tutorial')) {
|
|
||||||
function add_tutorial(array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->insert('tutorials', [
|
|
||||||
'title' => trim($payload['title'] ?? ''),
|
|
||||||
'description' => trim($payload['description'] ?? ''),
|
|
||||||
'status' => trim($payload['status'] ?? 'active'),
|
|
||||||
'file_path' => $payload['file_path'] ?? null,
|
|
||||||
'video_url' => trim($payload['video_url'] ?? ''),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('update_tutorial')) {
|
|
||||||
function update_tutorial(int $id, array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$data = [
|
|
||||||
'title' => trim($payload['title'] ?? ''),
|
|
||||||
'description' => trim($payload['description'] ?? ''),
|
|
||||||
'status' => trim($payload['status'] ?? 'active'),
|
|
||||||
'video_url' => trim($payload['video_url'] ?? ''),
|
|
||||||
];
|
|
||||||
if (!empty($payload['file_path'])) {
|
|
||||||
$data['file_path'] = $payload['file_path'];
|
|
||||||
}
|
|
||||||
$pdo->update('tutorials', $data, "id = ?", [$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('delete_tutorial')) {
|
|
||||||
function delete_tutorial(int $id): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$row = $pdo->getOne("SELECT file_path FROM tutorials WHERE id = ?", [$id]);
|
|
||||||
$filePath = $row['file_path'] ?? null;
|
|
||||||
$pdo->deleteById('tutorials', $id);
|
|
||||||
if ($filePath && is_string($filePath)) {
|
|
||||||
$full = __DIR__ . '/../' . ltrim($filePath, '/');
|
|
||||||
if (is_file($full)) {
|
|
||||||
@unlink($full);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('save_subscription')) {
|
|
||||||
function save_subscription(array $payload): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$pdo->rawQuery(
|
|
||||||
"INSERT INTO newsletter_subscriptions (first_name, email) VALUES (?, ?) ON DUPLICATE KEY UPDATE first_name = VALUES(first_name)",
|
|
||||||
[
|
|
||||||
trim($payload['first_name'] ?? ''),
|
|
||||||
trim($payload['email'] ?? ''),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('get_subscriptions')) {
|
|
||||||
function get_subscriptions(): array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
return $pdo->query("SELECT id, first_name, email, created_at FROM newsletter_subscriptions ORDER BY id DESC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('get_admin_by_email')) {
|
|
||||||
function get_admin_by_email(string $email): ?array
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$row = $pdo->getOne("SELECT * FROM admins WHERE email = ?", [$email]);
|
|
||||||
return $row ?: null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('create_admin')) {
|
|
||||||
function create_admin(string $email, string $password): void
|
|
||||||
{
|
|
||||||
$pdo = db();
|
|
||||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
|
||||||
$pdo->insert('admins', ['email' => $email, 'password_hash' => $hash]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once __DIR__ . '/mysqli-class.php';
|
|
||||||
|
|
||||||
function db(): MysqliDb
|
|
||||||
{
|
|
||||||
static $db = null;
|
|
||||||
if ($db instanceof MysqliDb) {
|
|
||||||
return $db;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = 'virtualuser';
|
|
||||||
$pass = 'ltnwGXZqvQJqXjPPQgmO';
|
|
||||||
$dbName = 'shabby_road';
|
|
||||||
$host = 'virtualengage.c1qdknkurtfn.ap-south-1.rds.amazonaws.com';
|
|
||||||
$port = 3306;
|
|
||||||
|
|
||||||
$attempts = [
|
|
||||||
['socket' => null, 'host' => $host, 'port' => $port],
|
|
||||||
];
|
|
||||||
|
|
||||||
$lastError = '';
|
|
||||||
foreach ($attempts as $attempt) {
|
|
||||||
try {
|
|
||||||
$db = new MysqliDb([
|
|
||||||
'host' => $attempt['host'],
|
|
||||||
'username' => $user,
|
|
||||||
'password' => $pass,
|
|
||||||
'db' => $dbName,
|
|
||||||
'port' => $attempt['port'],
|
|
||||||
'socket' => $attempt['socket'],
|
|
||||||
'charset' => 'utf8mb4',
|
|
||||||
]);
|
|
||||||
bootstrap_schema($db);
|
|
||||||
return $db;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$lastError = $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception('Database connection failed: ' . $lastError);
|
|
||||||
}
|
|
||||||
|
|
||||||
function bootstrap_schema(MysqliDb $db): void
|
|
||||||
{
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS admins (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
email VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
password_hash VARCHAR(255) NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
$resourceTable = ensure_resources_table($db);
|
|
||||||
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS sessions (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT NOT NULL,
|
|
||||||
tag VARCHAR(50) DEFAULT NULL,
|
|
||||||
cta_label VARCHAR(100) DEFAULT 'Register',
|
|
||||||
start_date DATE DEFAULT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS contacts (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
email VARCHAR(255) NOT NULL,
|
|
||||||
organization VARCHAR(255) DEFAULT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS newsletter_subscriptions (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
first_name VARCHAR(255) DEFAULT NULL,
|
|
||||||
email VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS tutorials (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT NOT NULL,
|
|
||||||
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
||||||
file_path VARCHAR(255) DEFAULT NULL,
|
|
||||||
video_url VARCHAR(255) DEFAULT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
|
|
||||||
seed_default_admin($db);
|
|
||||||
seed_sample_resources($db, $resourceTable);
|
|
||||||
seed_sample_sessions($db);
|
|
||||||
seed_sample_tutorials($db);
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed_default_admin(MysqliDb $db): void
|
|
||||||
{
|
|
||||||
$row = $db->getOne("SELECT 1 FROM admins WHERE email = ?", ['ravi@stratawiz.com']);
|
|
||||||
if ($row) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$hash = password_hash('qwerty', PASSWORD_DEFAULT);
|
|
||||||
$db->insert('admins', ['email' => 'ravi@stratawiz.com', 'password_hash' => $hash]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed_sample_resources(MysqliDb $db, string $table): void
|
|
||||||
{
|
|
||||||
$count = (int) $db->getValue("SELECT COUNT(*) as c FROM `$table`");
|
|
||||||
if ($count > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$samples = [
|
|
||||||
['title' => 'Understanding Lung Function in Adult Populations', 'summary' => 'A concise explainer on spirometry metrics and how to interpret lung function assessments in community settings.', 'label' => 'Publication'],
|
|
||||||
['title' => 'Lung Function in India: Overview', 'summary' => 'Key findings from multi-city research that maps prevalence of respiratory conditions across demographic groups.', 'label' => 'Research Brief'],
|
|
||||||
['title' => 'Childhood Lung Function in India', 'summary' => 'Practical guidance for pediatric screening teams and frontline clinicians working with young patients.', 'label' => 'Toolkit'],
|
|
||||||
['title' => 'Understanding Lung Function in Rural Populations', 'summary' => 'Field notes and methodologies for conducting door-to-door lung health studies with limited resources.', 'label' => 'Field Guide'],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($samples as $sample) {
|
|
||||||
$db->insert($table, $sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed_sample_sessions(MysqliDb $db): void
|
|
||||||
{
|
|
||||||
$count = (int) $db->getValue("SELECT COUNT(*) as c FROM sessions");
|
|
||||||
if ($count > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$samples = [
|
|
||||||
['title' => 'Nutrition guidance for advanced lung conditions', 'description' => 'Upcoming webinar on dietary support for patients with complex respiratory conditions.', 'tag' => 'New'],
|
|
||||||
['title' => 'Workshop: Field data quality for respiratory studies', 'description' => 'Hands-on clinic for research leads to strengthen field protocols and audits.', 'tag' => 'Update'],
|
|
||||||
['title' => 'Training cohort launch: Spirometry essentials', 'description' => 'New batch begins soon with live mentoring and case reviews.', 'tag' => 'Upcoming'],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($samples as $sample) {
|
|
||||||
$db->insert('sessions', $sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed_sample_tutorials(MysqliDb $db): void
|
|
||||||
{
|
|
||||||
$count = (int) $db->getValue("SELECT COUNT(*) as c FROM tutorials");
|
|
||||||
if ($count > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$samples = [
|
|
||||||
['title' => 'Spirometry Basics', 'description' => 'Step-by-step guide for accurate spirometry setup and testing.', 'status' => 'active'],
|
|
||||||
['title' => 'Inhaler Technique', 'description' => 'Visual walkthrough on patient education for inhaler use.', 'status' => 'active'],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($samples as $sample) {
|
|
||||||
$db->insert('tutorials', $sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensure_resources_table(MysqliDb $db): string
|
|
||||||
{
|
|
||||||
$candidates = ['resources_table', 'resources'];
|
|
||||||
|
|
||||||
// Prefer any existing table that already has rows.
|
|
||||||
foreach ($candidates as $name) {
|
|
||||||
$exists = $db->getValue("SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?", [$name]);
|
|
||||||
if ($exists) {
|
|
||||||
$count = (int) $db->getValue("SELECT COUNT(*) FROM `$name`");
|
|
||||||
if ($count > 0) {
|
|
||||||
add_resource_columns_if_missing($db, $name);
|
|
||||||
return $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to first existing, even if empty.
|
|
||||||
foreach ($candidates as $name) {
|
|
||||||
$exists = $db->getValue("SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?", [$name]);
|
|
||||||
if ($exists) {
|
|
||||||
add_resource_columns_if_missing($db, $name);
|
|
||||||
return $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = 'resources';
|
|
||||||
$db->rawQuery("CREATE TABLE IF NOT EXISTS `$name` (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
summary TEXT NOT NULL,
|
|
||||||
label VARCHAR(100) NOT NULL,
|
|
||||||
file_path VARCHAR(255) DEFAULT NULL,
|
|
||||||
video_url VARCHAR(255) DEFAULT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)");
|
|
||||||
add_resource_columns_if_missing($db, $name);
|
|
||||||
return $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
function add_resource_columns_if_missing(MysqliDb $db, string $table): void
|
|
||||||
{
|
|
||||||
$columns = [
|
|
||||||
'file_path' => 'ALTER TABLE `%s` ADD COLUMN file_path VARCHAR(255) DEFAULT NULL',
|
|
||||||
'video_url' => 'ALTER TABLE `%s` ADD COLUMN video_url VARCHAR(255) DEFAULT NULL',
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($columns as $column => $alter) {
|
|
||||||
$exists = $db->getValue("SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?", [$table, $column]);
|
|
||||||
if (!$exists) {
|
|
||||||
$db->rawQuery(sprintf($alter, $table));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resource_table_name(MysqliDb $db): string
|
|
||||||
{
|
|
||||||
static $resolved;
|
|
||||||
if ($resolved) {
|
|
||||||
return $resolved;
|
|
||||||
}
|
|
||||||
$resolved = ensure_resources_table($db);
|
|
||||||
return $resolved;
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<footer class="site-footer">
|
|
||||||
<div class="copyright">
|
|
||||||
@ CREST - Chest Research and Training Pyt Lad ·
|
|
||||||
<a href="#">Privacy Policy</a> |
|
|
||||||
<a href="#">Terms of use</a> |
|
|
||||||
<a href="#">Linkin</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="<?php echo $assetPathPrefix; ?>assets/js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
$assetPathPrefix = $assetPathPrefix ?? '';
|
|
||||||
$pageTitle = $pageTitle ?? 'Crest Lung Health';
|
|
||||||
$currentPage = $currentPage ?? '';
|
|
||||||
|
|
||||||
$navItems = [
|
|
||||||
['key' => 'home', 'label' => 'Home', 'href' => $assetPathPrefix . 'index.php'],
|
|
||||||
['key' => 'experts', 'label' => 'Our Experts', 'href' => $assetPathPrefix . 'experts.php'],
|
|
||||||
['key' => 'services', 'label' => 'Our Services', 'href' => $assetPathPrefix . 'services.php'],
|
|
||||||
['key' => 'training', 'label' => 'Training', 'href' => $assetPathPrefix . 'training.php'],
|
|
||||||
['key' => 'achievements', 'label' => 'Achievements', 'href' => $assetPathPrefix . 'achievements.php'],
|
|
||||||
['key' => 'resources', 'label' => 'Resources', 'href' => $assetPathPrefix . 'resources.php'],
|
|
||||||
['key' => 'news', 'label' => 'News & Events', 'href' => $assetPathPrefix . 'news-events.php'],
|
|
||||||
['key' => 'contact', 'label' => 'Contact', 'href' => $assetPathPrefix . 'contact.php'],
|
|
||||||
];
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="<?php echo $assetPathPrefix; ?>assets/css/style.css">
|
|
||||||
<?php if (!empty($extraStyles)) { echo $extraStyles; } ?>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header class="site-header">
|
|
||||||
<div class="container nav-bar">
|
|
||||||
<div class="brand">
|
|
||||||
<div class="brand-mark">C</div>
|
|
||||||
<div>
|
|
||||||
<div class="brand-name">CREST</div>
|
|
||||||
<div class="brand-tagline">Cancer Research Training & Consultancy</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<nav class="nav">
|
|
||||||
<?php foreach ($navItems as $item): ?>
|
|
||||||
<a href="<?php echo htmlspecialchars($item['href']); ?>"
|
|
||||||
class="<?php echo $currentPage === $item['key'] ? 'active' : ''; ?>">
|
|
||||||
<?php echo htmlspecialchars($item['label']); ?>
|
|
||||||
</a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
class MysqliDb
|
|
||||||
{
|
|
||||||
/** @var mysqli */
|
|
||||||
private $conn;
|
|
||||||
|
|
||||||
public function __construct(array $config)
|
|
||||||
{
|
|
||||||
$host = $config['host'] ?? '127.0.0.1';
|
|
||||||
$user = $config['username'] ?? 'root';
|
|
||||||
$pass = $config['password'] ?? '';
|
|
||||||
$db = $config['db'] ?? '';
|
|
||||||
$port = $config['port'] ?? null;
|
|
||||||
$socket = $config['socket'] ?? null;
|
|
||||||
$charset = $config['charset'] ?? 'utf8mb4';
|
|
||||||
|
|
||||||
$this->conn = @new mysqli($host, $user, $pass, $db, $port ?? ini_get("mysqli.default_port"), $socket);
|
|
||||||
if ($this->conn->connect_errno) {
|
|
||||||
throw new Exception('MySQL connection error: ' . $this->conn->connect_error);
|
|
||||||
}
|
|
||||||
$this->conn->set_charset($charset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function rawQuery(string $sql, array $params = [])
|
|
||||||
{
|
|
||||||
if (empty($params)) {
|
|
||||||
$result = $this->conn->query($sql);
|
|
||||||
if ($result === false) {
|
|
||||||
throw new Exception('Query error: ' . $this->conn->error);
|
|
||||||
}
|
|
||||||
if ($result === true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$data = $result->fetch_all(MYSQLI_ASSOC);
|
|
||||||
$result->free();
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $this->prepareAndBind($sql, $params);
|
|
||||||
if (!$stmt->execute()) {
|
|
||||||
throw new Exception('Query error: ' . $stmt->error);
|
|
||||||
}
|
|
||||||
$res = $stmt->get_result();
|
|
||||||
$rows = $res ? $res->fetch_all(MYSQLI_ASSOC) : [];
|
|
||||||
$stmt->close();
|
|
||||||
return $rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function query(string $sql, array $params = [])
|
|
||||||
{
|
|
||||||
return $this->rawQuery($sql, $params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
public function getOne(string $sql, array $params = [])
|
|
||||||
{
|
|
||||||
$rows = $this->rawQuery($sql, $params);
|
|
||||||
return $rows[0] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getValue(string $sql, array $params = [])
|
|
||||||
{
|
|
||||||
$row = $this->getOne($sql, $params);
|
|
||||||
if (!$row) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return array_shift($row);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insert(string $table, array $data): int
|
|
||||||
{
|
|
||||||
$cols = array_keys($data);
|
|
||||||
$placeholders = implode(',', array_fill(0, count($cols), '?'));
|
|
||||||
$colList = '`' . implode('`,`', $cols) . '`';
|
|
||||||
$sql = "INSERT INTO `$table` ($colList) VALUES ($placeholders)";
|
|
||||||
$stmt = $this->prepareAndBind($sql, array_values($data));
|
|
||||||
if (!$stmt->execute()) {
|
|
||||||
throw new Exception('Insert error: ' . $stmt->error);
|
|
||||||
}
|
|
||||||
$id = $stmt->insert_id ?: $this->conn->insert_id;
|
|
||||||
$stmt->close();
|
|
||||||
return (int) $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteById(string $table, int $id): bool
|
|
||||||
{
|
|
||||||
$stmt = $this->prepareAndBind("DELETE FROM `$table` WHERE id = ?", [$id]);
|
|
||||||
$ok = $stmt->execute();
|
|
||||||
$stmt->close();
|
|
||||||
return $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(string $table, array $data, string $where, array $params = []): bool
|
|
||||||
{
|
|
||||||
$sets = [];
|
|
||||||
$values = [];
|
|
||||||
foreach ($data as $col => $val) {
|
|
||||||
$sets[] = "`$col` = ?";
|
|
||||||
$values[] = $val;
|
|
||||||
}
|
|
||||||
$sql = "UPDATE `$table` SET " . implode(',', $sets) . " WHERE $where";
|
|
||||||
$bind = array_merge($values, $params);
|
|
||||||
$stmt = $this->prepareAndBind($sql, $bind);
|
|
||||||
$ok = $stmt->execute();
|
|
||||||
$stmt->close();
|
|
||||||
return $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function prepareAndBind(string $sql, array $params): mysqli_stmt
|
|
||||||
{
|
|
||||||
$stmt = $this->conn->prepare($sql);
|
|
||||||
if (!$stmt) {
|
|
||||||
throw new Exception('Prepare failed: ' . $this->conn->error);
|
|
||||||
}
|
|
||||||
if (!empty($params)) {
|
|
||||||
$types = '';
|
|
||||||
$refs = [];
|
|
||||||
foreach ($params as $param) {
|
|
||||||
$types .= is_int($param) ? 'i' : (is_float($param) ? 'd' : 's');
|
|
||||||
}
|
|
||||||
$refs[] = &$types;
|
|
||||||
foreach ($params as $k => $v) {
|
|
||||||
$refs[] = &$params[$k];
|
|
||||||
}
|
|
||||||
$stmt->bind_param(...$refs);
|
|
||||||
}
|
|
||||||
return $stmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<footer class="bg-success text-white py-3 mt-5">
|
|
||||||
<div class="container d-flex flex-wrap justify-content-between">
|
|
||||||
<small>@ CREST - Chest Research and Training Pyt Lad</small>
|
|
||||||
<div class="d-flex gap-3 small">
|
|
||||||
<a href="#" class="text-white text-decoration-none">Privacy Policy</a>
|
|
||||||
<a href="#" class="text-white text-decoration-none">Terms of use</a>
|
|
||||||
<a href="#" class="text-white text-decoration-none">Linkin</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="assets/js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
$pageTitle = $pageTitle ?? 'CREST';
|
|
||||||
$metaDescription = $metaDescription ?? 'CREST advances respiratory health through research, training, and collaboration.';
|
|
||||||
$canonical = '';
|
|
||||||
if (!empty($_SERVER['HTTP_HOST']) && !empty($_SERVER['REQUEST_URI'])) {
|
|
||||||
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
|
||||||
$canonical = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name="description" content="<?php echo htmlspecialchars($metaDescription, ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
<meta name="robots" content="index,follow">
|
|
||||||
<meta property="og:title" content="<?php echo htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
<meta property="og:description" content="<?php echo htmlspecialchars($metaDescription, ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
<?php if ($canonical): ?><link rel="canonical" href="<?php echo htmlspecialchars($canonical, ENT_QUOTES, 'UTF-8'); ?>"><?php endif; ?>
|
|
||||||
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="assets/css/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary shadow-sm sticky-top">
|
|
||||||
<div class="container">
|
|
||||||
<a class="navbar-brand d-flex align-items-center gap-2" href="index.php">
|
|
||||||
<div class="brand-mark text-white fw-bold d-flex align-items-center justify-content-center">C</div>
|
|
||||||
<div>
|
|
||||||
<div class="fw-bold text-success m-0 lh-1">CREST</div>
|
|
||||||
<small class="text-muted d-block" style="font-size:12px;">Chest Research and Training</small>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="mainNav">
|
|
||||||
<ul class="navbar-nav ms-auto mb-2 mb-lg-0 small fw-semibold">
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="index.php">Home</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="who-we-are.php">Who we are</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="expertise.php">Our Expertise</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="training.php">Training</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="achievements.php">Achievements</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="resources.php">Resources</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="work-with-us.php">Work with us</a></li>
|
|
||||||
<li class="nav-item"><a class="nav-link text-muted" href="contact.php">Contact</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
@ -1,282 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$resources = get_tutorials(); // reuse tutorials data for resources section
|
|
||||||
$sessions = get_sessions();
|
|
||||||
$pageTitle = 'CREST | Chest Research and Training';
|
|
||||||
$metaDescription = 'Explore CREST respiratory research, training programs, events, and downloadable resources to strengthen lung health initiatives.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="hero-section py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Transforming Respiratory Health Through</p>
|
|
||||||
<h1 class="fw-bold lh-1">Research, Training & Innovation</h1>
|
|
||||||
<p class="text-muted mb-4">Advancing the science of lung health to create a healthier India.</p>
|
|
||||||
<div class="d-flex flex-wrap gap-2 mb-4">
|
|
||||||
<a class="btn btn-outline-secondary rounded-3" href="#resources">Browse services</a>
|
|
||||||
<a class="btn btn-success text-white rounded-3" href="#contact">Get in touch</a>
|
|
||||||
</div>
|
|
||||||
<div class="row row-cols-2 row-cols-md-3 row-cols-lg-5 g-2">
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 h-100 rounded-4 bg-light-subtle">
|
|
||||||
<div class="fw-bold">1Mn+</div>
|
|
||||||
<div class="text-muted small">Lives touched</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 h-100 rounded-4 bg-light-subtle">
|
|
||||||
<div class="fw-bold">5k+</div>
|
|
||||||
<div class="text-muted small">Doctors trained</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 h-100 rounded-4 bg-light-subtle">
|
|
||||||
<div class="fw-bold">500+</div>
|
|
||||||
<div class="text-muted small">Medical schools</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 h-100 rounded-4 bg-light-subtle">
|
|
||||||
<div class="fw-bold">300+</div>
|
|
||||||
<div class="text-muted small">Research studies</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="stat-tile text-center p-3 h-100 rounded-4 bg-light-subtle">
|
|
||||||
<div class="fw-bold">50+</div>
|
|
||||||
<div class="text-muted small">New research</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6 text-center">
|
|
||||||
<div class="hero-orb mx-auto mb-3">
|
|
||||||
<div class="hero-orb-inner"></div>
|
|
||||||
</div>
|
|
||||||
<span class="badge text-bg-dark px-3 py-2 rounded-pill">Lab ready</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-5" id="who">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-md-5">
|
|
||||||
<div class="rounded-4 bg-light-subtle p-4 h-100 d-flex flex-column justify-content-between">
|
|
||||||
<div class="placeholder-img flex-grow-1 rounded-4 mb-3"></div>
|
|
||||||
<div class="d-flex align-items-center gap-2">
|
|
||||||
<span class="badge bg-success-subtle text-success fw-semibold">300+</span>
|
|
||||||
<span class="small text-muted">consultants & specialists</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-7">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Who we are</p>
|
|
||||||
<h2 class="fw-bold">Grounded in data, inspired by care</h2>
|
|
||||||
<p class="text-muted">We are a multidisciplinary team working at the intersection of research, training, and respiratory medicine. From field studies to clinical education, we help organizations make lung health programs measurable, effective, and equitable.</p>
|
|
||||||
<ul class="list-unstyled mb-3">
|
|
||||||
<li class="d-flex align-items-center gap-2 mb-2">
|
|
||||||
<span class="dot bg-success"></span>
|
|
||||||
<span class="fw-semibold">Modern Research Device</span>
|
|
||||||
</li>
|
|
||||||
<li class="d-flex align-items-center gap-2 mb-2">
|
|
||||||
<span class="dot bg-warning"></span>
|
|
||||||
<span class="fw-semibold">World Class laboratory</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<a class="btn btn-success text-white rounded-3" href="#expertise">Learn more</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-5 bg-light" id="expertise">
|
|
||||||
<div class="container">
|
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
|
|
||||||
<div>
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Our Expertise</p>
|
|
||||||
<h3 class="fw-bold">Our Services & Expertise</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Research</h5>
|
|
||||||
<p class="text-muted small mb-3">Rigorous studies to map lung health trends and inform community programs.</p>
|
|
||||||
<a class="text-success fw-semibold small" href="#">Learn more</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Training</h5>
|
|
||||||
<p class="text-muted small mb-3">Workshops and mentoring that upskill clinicians and field teams.</p>
|
|
||||||
<a class="text-success fw-semibold small" href="#">Learn more</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="icon-pill mb-3">★</div>
|
|
||||||
<h5 class="fw-bold">Consulting</h5>
|
|
||||||
<p class="text-muted small mb-3">Advisory for programs to scale respiratory care safely and effectively.</p>
|
|
||||||
<a class="text-success fw-semibold small" href="#">Learn more</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-5" id="training">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<h3 class="fw-bold">Training Program</h3>
|
|
||||||
<p class="text-muted">Our modular training brings together research-backed content, case-based discussions, and practical skill-building so teams can lead respiratory health initiatives confidently.</p>
|
|
||||||
<ul class="list-unstyled text-muted small mb-3">
|
|
||||||
<li class="mb-1">• Research Training</li>
|
|
||||||
<li class="mb-1">• Spirometry & Lung Function Testing</li>
|
|
||||||
<li class="mb-1">• Critical Situations</li>
|
|
||||||
<li class="mb-1">• Audit Program</li>
|
|
||||||
<li class="mb-1">• Quality Assurance</li>
|
|
||||||
</ul>
|
|
||||||
<a class="btn btn-success text-white rounded-3">Join the training</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="rounded-4 bg-light-subtle p-3">
|
|
||||||
<div class="placeholder-img tall rounded-4 mb-3"></div>
|
|
||||||
<span class="badge bg-success-subtle text-success fw-semibold">100+ batch completions</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-5 bg-body" id="events">
|
|
||||||
<div class="container">
|
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
|
|
||||||
<div>
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Latest Events</p>
|
|
||||||
<h3 class="fw-bold">Latest Events</h3>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-outline-success" href="sessions-list.php">View all events</a>
|
|
||||||
</div>
|
|
||||||
<?php if (empty($sessions)): ?>
|
|
||||||
<p class="text-muted">No upcoming sessions yet. Check back soon.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="row g-3">
|
|
||||||
<?php foreach ($sessions as $session): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($session)) { continue; }
|
|
||||||
$number = htmlspecialchars((string)($session['number'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$startDate = !empty($session['start_date']) ? date('M d, Y', strtotime($session['start_date'])) : '';
|
|
||||||
$venue = htmlspecialchars((string)($session['venue'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$code = htmlspecialchars((string)($session['code'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$regLink = $code ? "https://road.xltr.in/register/{$code}" : '#';
|
|
||||||
?>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4">
|
|
||||||
<div class="placeholder-img small rounded-top-4"></div>
|
|
||||||
<div class="card-body position-relative">
|
|
||||||
<span class="badge bg-white border text-success position-absolute top-0 end-0 m-3">Upcoming</span>
|
|
||||||
<h6 class="fw-bold mt-3">Session <?php echo $number ?: 'N/A'; ?></h6>
|
|
||||||
<?php if ($startDate): ?><p class="text-muted small mb-1">Starts: <?php echo $startDate; ?></p><?php endif; ?>
|
|
||||||
<?php if ($venue): ?><p class="text-muted small mb-2">Venue: <?php echo $venue; ?></p><?php endif; ?>
|
|
||||||
<a class="btn btn-outline-success btn-sm mt-2" href="<?php echo $regLink; ?>" target="_blank">Register</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-5" id="resources">
|
|
||||||
<div class="container">
|
|
||||||
<div class="mb-4">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Resources for Learning and Research</p>
|
|
||||||
<h3 class="fw-bold">Resources for Learning and Research</h3>
|
|
||||||
<p class="text-muted small">Well-curated resources to explore our work and keep you up to date on our research.</p>
|
|
||||||
</div>
|
|
||||||
<?php if (empty($resources)): ?>
|
|
||||||
<p class="text-muted">No resources yet.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="row g-3">
|
|
||||||
<?php foreach ($resources as $resource): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($resource)) { continue; }
|
|
||||||
$label = htmlspecialchars((string)($resource['label'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$title = htmlspecialchars((string)($resource['title'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$summary = htmlspecialchars((string)($resource['description'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$filePath = isset($resource['file_path']) ? (string)$resource['file_path'] : '';
|
|
||||||
$videoUrl = isset($resource['video_url']) ? (string)$resource['video_url'] : '';
|
|
||||||
?>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="card h-100 shadow-sm border-0 rounded-4 p-3">
|
|
||||||
<span class="badge bg-success-subtle text-success fw-semibold mb-2"><?php echo $label; ?></span>
|
|
||||||
<h6 class="fw-bold"><?php echo $title; ?></h6>
|
|
||||||
<p class="text-muted small mb-3"><?php echo $summary; ?></p>
|
|
||||||
<div class="d-flex gap-2 flex-wrap">
|
|
||||||
<?php if ($filePath): ?>
|
|
||||||
<a class="btn btn-outline-success btn-sm" href="<?php echo htmlspecialchars(ltrim($filePath, '/'), ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Download</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($videoUrl): ?>
|
|
||||||
<a class="btn btn-success btn-sm text-white" href="<?php echo htmlspecialchars($videoUrl, ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Watch</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!$filePath && !$videoUrl): ?>
|
|
||||||
<span class="text-muted small">No download yet</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section class="py-5 bg-success-subtle" id="contact">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Subscribe</p>
|
|
||||||
<h3 class="fw-bold">Subscribe to Regular Newsletters.</h3>
|
|
||||||
<p class="text-muted">Get curated research insights and training updates straight to your inbox.</p>
|
|
||||||
<?php if (!empty($newsletterMessage)): ?>
|
|
||||||
<div class="alert alert-success p-2"><?php echo htmlspecialchars($newsletterMessage); ?></div>
|
|
||||||
<?php elseif (!empty($newsletterError)): ?>
|
|
||||||
<div class="alert alert-danger p-2"><?php echo htmlspecialchars($newsletterError); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<form class="row g-2" method="post">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<input type="text" class="form-control rounded-3" name="newsletter_name" placeholder="First Name">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-5">
|
|
||||||
<input type="email" class="form-control rounded-3" name="newsletter_email" placeholder="Email" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 d-grid">
|
|
||||||
<button class="btn btn-success text-white rounded-3" type="submit">Subscribe</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6 text-center">
|
|
||||||
<div class="placeholder-img mailer rounded-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'News & Events | CREST';
|
|
||||||
$metaDescription = 'Stay updated on CREST respiratory health events and upcoming training sessions.';
|
|
||||||
$sessions = get_sessions();
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5 bg-light">
|
|
||||||
<div class="container">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">News & Events</p>
|
|
||||||
<h1 class="fw-bold">Latest updates, workshops, and announcements</h1>
|
|
||||||
<?php if (empty($sessions)): ?>
|
|
||||||
<p class="text-muted" style="margin-top:16px;">No sessions announced yet. Check back soon.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="row g-3" style="margin-top:16px;">
|
|
||||||
<?php foreach ($sessions as $session): ?>
|
|
||||||
<?php
|
|
||||||
$number = htmlspecialchars((string)($session['number'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$startDate = !empty($session['start_date']) ? date('M d, Y', strtotime($session['start_date'])) : '';
|
|
||||||
$venue = htmlspecialchars((string)($session['venue'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$code = htmlspecialchars((string)($session['code'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$regLink = $code ? "https://road.xltr.in/register/{$code}" : '#';
|
|
||||||
?>
|
|
||||||
<article class="card event-card col-md-4">
|
|
||||||
<div class="placeholder-img small"></div>
|
|
||||||
<div class="chip">Upcoming</div>
|
|
||||||
<h3><?php echo $number ? 'Session ' . $number : 'Session'; ?></h3>
|
|
||||||
<?php if ($startDate): ?><p class="text-muted small mb-1">Starts: <?php echo $startDate; ?></p><?php endif; ?>
|
|
||||||
<?php if ($venue): ?><p class="text-muted small mb-2">Venue: <?php echo $venue; ?></p><?php endif; ?>
|
|
||||||
<a class="btn btn-outline-success btn-sm" href="<?php echo $regLink; ?>" target="_blank">Register</a>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Resources | CREST';
|
|
||||||
$metaDescription = 'Download CREST respiratory learning resources including tutorials, guides, and video sessions.';
|
|
||||||
$resources = get_tutorials(false); // reuse tutorials data for resources page
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Resources</p>
|
|
||||||
<h1 class="fw-bold">Resources for Learning and Research</h1>
|
|
||||||
<p class="text-muted">Downloadable guides, publications, and toolkits created by our faculty to support your teams.</p>
|
|
||||||
|
|
||||||
<?php if (empty($resources)): ?>
|
|
||||||
<p class="text-muted" style="margin-top:16px;">No resources yet. Check back soon.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="row g-3" style="margin-top:20px;">
|
|
||||||
<?php foreach ($resources as $resource): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($resource)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$label = htmlspecialchars((string)($resource['label'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$title = htmlspecialchars((string)($resource['title'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$summary = htmlspecialchars((string)($resource['description'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$filePath = isset($resource['file_path']) ? (string)$resource['file_path'] : '';
|
|
||||||
$videoUrl = isset($resource['video_url']) ? (string)$resource['video_url'] : '';
|
|
||||||
?>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<article class="card h-100 shadow-sm border-0 rounded-4 p-3">
|
|
||||||
<span class="badge bg-success-subtle text-success fw-semibold mb-2"><?php echo $label ?: 'Resource'; ?></span>
|
|
||||||
<h3 class="h5 fw-bold"><?php echo $title; ?></h3>
|
|
||||||
<p class="text-muted small mb-3"><?php echo $summary; ?></p>
|
|
||||||
<div class="d-flex gap-2 flex-wrap">
|
|
||||||
<?php if ($filePath): ?>
|
|
||||||
<a class="btn btn-outline-success btn-sm" href="<?php echo htmlspecialchars(ltrim($filePath, '/'), ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Download</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($videoUrl): ?>
|
|
||||||
<a class="btn btn-success btn-sm text-white" href="<?php echo htmlspecialchars($videoUrl, ENT_QUOTES, 'UTF-8'); ?>" target="_blank">Watch</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!$filePath && !$videoUrl): ?>
|
|
||||||
<span class="text-muted small">No download yet</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
$pageTitle = 'Our Services | Crest Lung Health';
|
|
||||||
$currentPage = 'services';
|
|
||||||
require __DIR__ . '/includes/header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="section light">
|
|
||||||
<div class="container">
|
|
||||||
<p class="eyebrow">Our Services</p>
|
|
||||||
<h1>Integrated solutions for lung health programs</h1>
|
|
||||||
<p class="body">From research design to clinical training and program evaluation, our services are built to deliver measurable outcomes.</p>
|
|
||||||
|
|
||||||
<div class="card-grid" style="margin-top:20px;">
|
|
||||||
<article class="card">
|
|
||||||
<div class="icon-pill">R</div>
|
|
||||||
<h3>Research Design</h3>
|
|
||||||
<p>Protocol development, ethics submissions, and analytics frameworks tailored to your population.</p>
|
|
||||||
</article>
|
|
||||||
<article class="card">
|
|
||||||
<div class="icon-pill">T</div>
|
|
||||||
<h3>Training & Upskilling</h3>
|
|
||||||
<p>Workshops, certifications, and blended learning pathways for clinicians and field teams.</p>
|
|
||||||
</article>
|
|
||||||
<article class="card">
|
|
||||||
<div class="icon-pill">Q</div>
|
|
||||||
<h3>Quality & Audits</h3>
|
|
||||||
<p>End-to-end audits, standard operating procedures, and continuous improvement playbooks.</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Events | CREST';
|
|
||||||
$metaDescription = 'Browse all current and upcoming CREST sessions with dates, venues, and registration links.';
|
|
||||||
$sessions = get_sessions();
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5 bg-light">
|
|
||||||
<div class="container">
|
|
||||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
|
|
||||||
<div>
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Events</p>
|
|
||||||
<h1 class="fw-bold">Current & Upcoming Sessions</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php if (empty($sessions)): ?>
|
|
||||||
<p class="text-muted" style="margin-top:16px;">No sessions announced yet. Check back soon.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="row g-3" style="margin-top:16px;">
|
|
||||||
<?php foreach ($sessions as $session): ?>
|
|
||||||
<?php
|
|
||||||
if (!is_array($session)) { continue; }
|
|
||||||
$number = htmlspecialchars((string)($session['number'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$startDate = !empty($session['start_date']) ? date('M d, Y', strtotime($session['start_date'])) : '';
|
|
||||||
$venue = htmlspecialchars((string)($session['venue'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$code = htmlspecialchars((string)($session['code'] ?? ''), ENT_QUOTES, 'UTF-8');
|
|
||||||
$regLink = $code ? "https://road.xltr.in/register/{$code}" : '#';
|
|
||||||
?>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<article class="card event-card h-100">
|
|
||||||
<div class="placeholder-img small"></div>
|
|
||||||
<div class="chip">Upcoming</div>
|
|
||||||
<div class="p-3">
|
|
||||||
<h5 class="fw-bold"><?php echo $number ? 'Session ' . $number : 'Session'; ?></h5>
|
|
||||||
<?php if ($startDate): ?><p class="text-muted small mb-1">Starts: <?php echo $startDate; ?></p><?php endif; ?>
|
|
||||||
<?php if ($venue): ?><p class="text-muted small mb-2">Venue: <?php echo $venue; ?></p><?php endif; ?>
|
|
||||||
<a class="btn btn-outline-success btn-sm" href="<?php echo $regLink; ?>" target="_blank">Register</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Training | CREST';
|
|
||||||
$metaDescription = 'Join CREST respiratory training programs covering spirometry, critical care, quality assurance, and field research.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<h3 class="fw-bold">Training Program</h3>
|
|
||||||
<p class="text-muted">Our modular training brings together research-backed content, case-based discussions, and practical skill-building so teams can lead respiratory health initiatives confidently.</p>
|
|
||||||
<ul class="list-unstyled text-muted small mb-3">
|
|
||||||
<li class="mb-1">• Research Training</li>
|
|
||||||
<li class="mb-1">• Spirometry & Lung Function Testing</li>
|
|
||||||
<li class="mb-1">• Critical Situations</li>
|
|
||||||
<li class="mb-1">• Audit Program</li>
|
|
||||||
<li class="mb-1">• Quality Assurance</li>
|
|
||||||
</ul>
|
|
||||||
<a class="btn btn-success text-white rounded-3">Join the training</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="placeholder-img tall rounded-4 mb-3"></div>
|
|
||||||
<span class="badge bg-success-subtle text-success fw-semibold">100+ batch completions</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
Binary file not shown.
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Who we are | CREST';
|
|
||||||
$metaDescription = 'Learn about CREST and our multidisciplinary team focused on advancing respiratory health through research and training.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row g-4 align-items-center">
|
|
||||||
<div class="col-md-5">
|
|
||||||
<div class="placeholder-img tall rounded-4 mb-3"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-7">
|
|
||||||
<p class="text-uppercase text-success fw-bold small mb-1">Who we are</p>
|
|
||||||
<h2 class="fw-bold">Grounded in data, inspired by care</h2>
|
|
||||||
<p class="text-muted">We are a multidisciplinary team working at the intersection of research, training, and respiratory medicine. From field studies to clinical education, we help organizations make lung health programs measurable, effective, and equitable.</p>
|
|
||||||
<ul class="list-unstyled mb-3">
|
|
||||||
<li class="mb-2">• Modern research device set-up</li>
|
|
||||||
<li class="mb-2">• World-class laboratory standards</li>
|
|
||||||
</ul>
|
|
||||||
<a class="btn btn-success text-white rounded-3" href="expertise.php">Explore expertise</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once __DIR__ . '/includes/data.php';
|
|
||||||
$pageTitle = 'Work with us | CREST';
|
|
||||||
$metaDescription = 'Partner with CREST for research collaborations, training partnerships, and respiratory program consulting.';
|
|
||||||
include __DIR__ . '/includes/site-header.php';
|
|
||||||
?>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="py-5">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="fw-bold mb-3">Work with us</h2>
|
|
||||||
<p class="text-muted mb-4">Partner with us on research, training programs, or pilot initiatives to strengthen respiratory health outcomes.</p>
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card shadow-sm border-0 rounded-4 h-100">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="fw-bold">Research collaborations</h5>
|
|
||||||
<p class="text-muted small mb-0">Co-design studies, data collection, and analysis with our research team.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card shadow-sm border-0 rounded-4 h-100">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="fw-bold">Training partnerships</h5>
|
|
||||||
<p class="text-muted small mb-0">Upskill your teams with modular curricula and faculty-led workshops.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="card shadow-sm border-0 rounded-4 h-100">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="fw-bold">Program consulting</h5>
|
|
||||||
<p class="text-muted small mb-0">Set up or scale clinics, QA processes, and field operations with our experts.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/site-footer.php'; ?>
|
|
||||||
Loading…
Reference in New Issue