Deploy on 2026-06-05 15:23:02
This commit is contained in:
@@ -5,18 +5,71 @@ namespace App\Controllers\Admin;
|
|||||||
use App\Controllers\Controller;
|
use App\Controllers\Controller;
|
||||||
use App\Core\Request;
|
use App\Core\Request;
|
||||||
use App\Core\Response;
|
use App\Core\Response;
|
||||||
|
use App\Services\Database\Connection;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
class DashboardController extends Controller
|
class DashboardController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
private PDO $pdo;
|
||||||
* Display admin dashboard.
|
|
||||||
*/
|
public function __construct(Connection $connection)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->pdo = $connection->getPdo();
|
||||||
|
}
|
||||||
|
|
||||||
public function index(Request $request, Response $response): string
|
public function index(Request $request, Response $response): string
|
||||||
{
|
{
|
||||||
$user = $request->routeParam('_authenticated_user');
|
$user = $request->routeParam('_authenticated_user');
|
||||||
|
$lang = $this->session->get('lang', 'en');
|
||||||
|
|
||||||
|
// Real stats from database
|
||||||
|
$orgCount = (int)$this->pdo->query("SELECT COUNT(*) FROM organizations WHERE deleted_at IS NULL")->fetchColumn();
|
||||||
|
$vcCount = (int)$this->pdo->query("SELECT COUNT(*) FROM organizations WHERE type='vc' AND deleted_at IS NULL")->fetchColumn();
|
||||||
|
$acceleratorCount = (int)$this->pdo->query("SELECT COUNT(*) FROM organizations WHERE type='accelerator' AND deleted_at IS NULL")->fetchColumn();
|
||||||
|
$opportunityCount = (int)$this->pdo->query("SELECT COUNT(*) FROM opportunities WHERE deleted_at IS NULL AND status='active'")->fetchColumn();
|
||||||
|
$contactCount = (int)$this->pdo->query("SELECT COUNT(*) FROM contacts WHERE deleted_at IS NULL")->fetchColumn();
|
||||||
|
$sourceCount = (int)$this->pdo->query("SELECT COUNT(*) FROM sources WHERE status='active'")->fetchColumn();
|
||||||
|
$todayOpps = (int)$this->pdo->query("SELECT COUNT(*) FROM opportunities WHERE DATE(created_at) = CURDATE()")->fetchColumn();
|
||||||
|
|
||||||
|
// Recent opportunities
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT o.*, org.name as org_name FROM opportunities o
|
||||||
|
LEFT JOIN organizations org ON org.id = o.organization_id
|
||||||
|
WHERE o.deleted_at IS NULL
|
||||||
|
ORDER BY o.created_at DESC LIMIT 10"
|
||||||
|
);
|
||||||
|
$recentOpps = $stmt->fetchAll() ?: [];
|
||||||
|
|
||||||
|
// Opportunities by type
|
||||||
|
$byType = $this->pdo->query(
|
||||||
|
"SELECT type, COUNT(*) as count FROM opportunities WHERE deleted_at IS NULL GROUP BY type"
|
||||||
|
)->fetchAll(PDO::FETCH_KEY_PAIR) ?: [];
|
||||||
|
|
||||||
|
// Recent activity
|
||||||
|
$stmt = $this->pdo->query("SELECT * FROM activity_logs ORDER BY created_at DESC LIMIT 10");
|
||||||
|
$recentActivities = $stmt->fetchAll() ?: [];
|
||||||
|
|
||||||
|
$langFile = __DIR__ . "/../../resources/lang/{$lang}.php";
|
||||||
|
$t = file_exists($langFile) ? require $langFile : [];
|
||||||
|
|
||||||
return $this->render('admin/dashboard', [
|
return $this->render('admin/dashboard', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'title' => 'Dashboard',
|
'title' => $t['dashboard'] ?? 'Dashboard',
|
||||||
|
't' => $t,
|
||||||
|
'lang' => $lang,
|
||||||
|
'stats' => [
|
||||||
|
'organizations' => $orgCount,
|
||||||
|
'vc' => $vcCount,
|
||||||
|
'accelerators' => $acceleratorCount,
|
||||||
|
'opportunities' => $opportunityCount,
|
||||||
|
'contacts' => $contactCount,
|
||||||
|
'sources' => $sourceCount,
|
||||||
|
'today' => $todayOpps,
|
||||||
|
],
|
||||||
|
'recent_opportunities' => $recentOpps,
|
||||||
|
'opportunities_by_type' => $byType,
|
||||||
|
'recent_activities' => $recentActivities,
|
||||||
], 'admin');
|
], 'admin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
97
app/Controllers/Admin/SettingsController.php
Normal file
97
app/Controllers/Admin/SettingsController.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Controllers\Controller;
|
||||||
|
use App\Core\Request;
|
||||||
|
use App\Core\Response;
|
||||||
|
use App\Services\Database\Connection;
|
||||||
|
use App\Services\Notification\TelegramNotifier;
|
||||||
|
use PDO;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class SettingsController extends Controller
|
||||||
|
{
|
||||||
|
private PDO $pdo;
|
||||||
|
private TelegramNotifier $notifier;
|
||||||
|
|
||||||
|
public function __construct(Connection $connection, TelegramNotifier $notifier)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->pdo = $connection->getPdo();
|
||||||
|
$this->notifier = $notifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Request $request, Response $response): string
|
||||||
|
{
|
||||||
|
$lang = $this->session->get('lang', 'en');
|
||||||
|
$langFile = __DIR__ . "/../../resources/lang/{$lang}.php";
|
||||||
|
$t = file_exists($langFile) ? require $langFile : [];
|
||||||
|
|
||||||
|
// Get telegram settings from database
|
||||||
|
$tgToken = $this->getSetting('telegram_bot_token', '');
|
||||||
|
$tgChatId = $this->getSetting('telegram_chat_id', '');
|
||||||
|
$tgEnabled = $this->getSetting('telegram_enabled', '0');
|
||||||
|
|
||||||
|
return $this->render('admin/settings/index', [
|
||||||
|
't' => $t,
|
||||||
|
'lang' => $lang,
|
||||||
|
'tg_token' => $tgToken,
|
||||||
|
'tg_chat_id' => $tgChatId,
|
||||||
|
'tg_enabled' => $tgEnabled,
|
||||||
|
], 'admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(Request $request, Response $response): void
|
||||||
|
{
|
||||||
|
$tgToken = $request->post('telegram_bot_token', '');
|
||||||
|
$tgChatId = $request->post('telegram_chat_id', '');
|
||||||
|
$tgEnabled = $request->post('telegram_enabled', '0');
|
||||||
|
|
||||||
|
$this->saveSetting('telegram_bot_token', $tgToken);
|
||||||
|
$this->saveSetting('telegram_chat_id', $tgChatId);
|
||||||
|
$this->saveSetting('telegram_enabled', $tgEnabled);
|
||||||
|
|
||||||
|
$this->session->setFlash('success', 'Settings saved successfully.');
|
||||||
|
$response->redirect('/admin/settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTelegram(Request $request, Response $response): void
|
||||||
|
{
|
||||||
|
$tgToken = $request->post('telegram_bot_token', '');
|
||||||
|
$tgChatId = $request->post('telegram_chat_id', '');
|
||||||
|
|
||||||
|
$this->notifier->configure($tgToken, $tgChatId);
|
||||||
|
|
||||||
|
if ($this->notifier->sendTest()) {
|
||||||
|
$this->session->setFlash('success', 'Test notification sent to Telegram!');
|
||||||
|
} else {
|
||||||
|
$this->session->setFlash('error', 'Failed to send Telegram notification. Check your token and chat ID.');
|
||||||
|
}
|
||||||
|
$response->redirect('/admin/settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function switchLang(Request $request, Response $response): void
|
||||||
|
{
|
||||||
|
$lang = $request->get('lang', 'en');
|
||||||
|
if (in_array($lang, ['ar', 'en'])) {
|
||||||
|
$this->session->set('lang', $lang);
|
||||||
|
}
|
||||||
|
$ref = $request->getHeader('Referer') ?? '/admin/dashboard';
|
||||||
|
$response->redirect($ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSetting(string $key, string $default = ''): string
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare("SELECT `value` FROM settings WHERE `key` = ?");
|
||||||
|
$stmt->execute([$key]);
|
||||||
|
$val = $stmt->fetchColumn();
|
||||||
|
return $val !== false ? $val : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveSetting(string $key, string $value): void
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare("INSERT INTO settings (`key`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = ?");
|
||||||
|
$stmt->execute([$key, $value, $value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,8 +75,9 @@ class SourcesController extends Controller
|
|||||||
$response->redirect('/admin/sources');
|
$response->redirect('/admin/sources');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(Request $request, Response $response, int $id): void
|
public function run(Request $request, Response $response): void
|
||||||
{
|
{
|
||||||
|
$id = (int)$request->routeParam('id');
|
||||||
$stmt = $this->pdo->prepare("SELECT * FROM sources WHERE id = ?");
|
$stmt = $this->pdo->prepare("SELECT * FROM sources WHERE id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$source = $stmt->fetch();
|
$source = $stmt->fetch();
|
||||||
|
|||||||
116
app/Services/Notification/TelegramNotifier.php
Normal file
116
app/Services/Notification/TelegramNotifier.php
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Notification;
|
||||||
|
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class TelegramNotifier
|
||||||
|
{
|
||||||
|
private ?string $botToken;
|
||||||
|
private ?string $chatId;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? null;
|
||||||
|
$this->chatId = $_ENV['TELEGRAM_CHAT_ID'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure from database settings.
|
||||||
|
*/
|
||||||
|
public function configure(string $botToken, string $chatId): void
|
||||||
|
{
|
||||||
|
$this->botToken = $botToken;
|
||||||
|
$this->chatId = $chatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a notification to Telegram.
|
||||||
|
*/
|
||||||
|
public function send(string $message, string $type = 'info'): bool
|
||||||
|
{
|
||||||
|
if (!$this->botToken || !$this->chatId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$icons = [
|
||||||
|
'info' => "\xF0\x9F\x93\xA1", // 📡
|
||||||
|
'success' => "\xE2\x9C\x85", // ✅
|
||||||
|
'warning' => "\xE2\x9A\xA0\xEF\xB8\x8F", // ⚠️
|
||||||
|
'error' => "\xE2\x9D\x8C", // ❌
|
||||||
|
'opportunity' => "\xF0\x9F\x92\xA1", // 💡
|
||||||
|
'funding' => "\xF0\x9F\x92\xB0", // 💰
|
||||||
|
];
|
||||||
|
|
||||||
|
$icon = $icons[$type] ?? $icons['info'];
|
||||||
|
$fullMessage = "{$icon} *ScoutIQ*\n\n{$message}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$url = "https://api.telegram.org/bot{$this->botToken}/sendMessage";
|
||||||
|
$payload = json_encode([
|
||||||
|
'chat_id' => $this->chatId,
|
||||||
|
'text' => $fullMessage,
|
||||||
|
'parse_mode' => 'Markdown',
|
||||||
|
'disable_web_page_preview' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$context = stream_context_create([
|
||||||
|
'http' => [
|
||||||
|
'method' => 'POST',
|
||||||
|
'header' => "Content-Type: application/json\r\n",
|
||||||
|
'content' => $payload,
|
||||||
|
'timeout' => 10,
|
||||||
|
],
|
||||||
|
'ssl' => ['verify_peer' => false, 'verify_peer_name' => false],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = @file_get_contents($url, false, $context);
|
||||||
|
return $response !== false;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify about a new opportunity.
|
||||||
|
*/
|
||||||
|
public function notifyNewOpportunity(array $opportunity): void
|
||||||
|
{
|
||||||
|
$title = $opportunity['title'] ?? 'Untitled';
|
||||||
|
$type = $opportunity['type'] ?? 'other';
|
||||||
|
$score = $opportunity['score'] ?? 0;
|
||||||
|
$url = $opportunity['url'] ?? '';
|
||||||
|
$desc = mb_substr($opportunity['description'] ?? '', 0, 200);
|
||||||
|
$orgName = $opportunity['org_name'] ?? '';
|
||||||
|
|
||||||
|
$message = "*New Opportunity:* {$title}\n";
|
||||||
|
$message .= "*Type:* {$type} | *Score:* {$score}/100\n";
|
||||||
|
if ($orgName) $message .= "*Organization:* {$orgName}\n";
|
||||||
|
if ($desc) $message .= "_{$desc}_\n";
|
||||||
|
if ($url) $message .= "\n[Open Link]({$url})";
|
||||||
|
|
||||||
|
$this->send($message, 'opportunity');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify about collector results.
|
||||||
|
*/
|
||||||
|
public function notifyCollectorResults(array $results): void
|
||||||
|
{
|
||||||
|
$message = "*Data Collection Complete*\n\n";
|
||||||
|
$message .= "Sources: {$results['processed']}/{$results['total_sources']}\n";
|
||||||
|
$message .= "New Opportunities: {$results['new_opportunities']}\n";
|
||||||
|
$message .= "New Organizations: {$results['new_organizations']}\n";
|
||||||
|
$message .= "Errors: {$results['errors']}";
|
||||||
|
|
||||||
|
$this->send($message, 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send test message.
|
||||||
|
*/
|
||||||
|
public function sendTest(): bool
|
||||||
|
{
|
||||||
|
return $this->send("Test notification from ScoutIQ. Your Telegram integration is working correctly!", 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ use App\Controllers\Admin\OrganizationsController;
|
|||||||
use App\Controllers\Admin\OpportunitiesController;
|
use App\Controllers\Admin\OpportunitiesController;
|
||||||
use App\Controllers\Admin\ContactsController;
|
use App\Controllers\Admin\ContactsController;
|
||||||
use App\Controllers\Admin\SourcesController;
|
use App\Controllers\Admin\SourcesController;
|
||||||
|
use App\Controllers\Admin\SettingsController;
|
||||||
use App\Middleware\SecurityHeaders;
|
use App\Middleware\SecurityHeaders;
|
||||||
use App\Middleware\RateLimit;
|
use App\Middleware\RateLimit;
|
||||||
use App\Middleware\CsrfProtection;
|
use App\Middleware\CsrfProtection;
|
||||||
@@ -72,6 +73,13 @@ $app->router->group([
|
|||||||
$r->post('/sources/{id}/update', [SourcesController::class, 'store']);
|
$r->post('/sources/{id}/update', [SourcesController::class, 'store']);
|
||||||
$r->get('/sources/{id}/delete', [SourcesController::class, 'delete']);
|
$r->get('/sources/{id}/delete', [SourcesController::class, 'delete']);
|
||||||
$r->get('/sources/{id}/run', [SourcesController::class, 'run']);
|
$r->get('/sources/{id}/run', [SourcesController::class, 'run']);
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
$r->get('/settings', [SettingsController::class, 'index']);
|
||||||
|
$r->post('/settings/save', [SettingsController::class, 'save']);
|
||||||
|
|
||||||
|
// Language switch (no CSRF needed for GET)
|
||||||
|
$r->get('/lang/{lang}', [SettingsController::class, 'switchLang']);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Logout endpoint
|
// Logout endpoint
|
||||||
|
|||||||
74
resources/lang/ar.php
Normal file
74
resources/lang/ar.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dashboard' => 'لوحة التحكم',
|
||||||
|
'organizations' => 'المنظمات',
|
||||||
|
'contacts' => 'جهات الاتصال',
|
||||||
|
'opportunities' => 'الفرص',
|
||||||
|
'sources' => 'مصادر البيانات',
|
||||||
|
'settings' => 'الإعدادات',
|
||||||
|
'logout' => 'تسجيل خروج',
|
||||||
|
'welcome' => 'مرحباً',
|
||||||
|
'add' => 'إضافة',
|
||||||
|
'edit' => 'تعديل',
|
||||||
|
'delete' => 'حذف',
|
||||||
|
'save' => 'حفظ',
|
||||||
|
'cancel' => 'إلغاء',
|
||||||
|
'search' => 'بحث',
|
||||||
|
'filter' => 'تصفية',
|
||||||
|
'name' => 'الاسم',
|
||||||
|
'type' => 'النوع',
|
||||||
|
'status' => 'الحالة',
|
||||||
|
'country' => 'الدولة',
|
||||||
|
'city' => 'المدينة',
|
||||||
|
'url' => 'الرابط',
|
||||||
|
'description' => 'الوصف',
|
||||||
|
'score' => 'النتيجة',
|
||||||
|
'created' => 'تاريخ الإنشاء',
|
||||||
|
'updated' => 'آخر تحديث',
|
||||||
|
'actions' => 'إجراءات',
|
||||||
|
'no_data' => 'لا توجد بيانات',
|
||||||
|
'total_organizations' => 'إجمالي المنظمات',
|
||||||
|
'total_opportunities' => 'إجمالي الفرص',
|
||||||
|
'total_contacts' => 'إجمالي جهات الاتصال',
|
||||||
|
'active_sources' => 'المصادر النشطة',
|
||||||
|
'today_opportunities' => 'فرص اليوم',
|
||||||
|
'vc_funds' => 'صناديق استثمار',
|
||||||
|
'accelerators' => 'مسرعات',
|
||||||
|
'recent_opportunities' => 'آخر الفرص',
|
||||||
|
'recent_activities' => 'آخر النشاطات',
|
||||||
|
'view_all' => 'عرض الكل',
|
||||||
|
'run_collector' => 'تشغيل الجمع',
|
||||||
|
'last_collected' => 'آخر جمع',
|
||||||
|
'platform_mode' => 'نمط المنصة',
|
||||||
|
'local' => 'محلي',
|
||||||
|
'production' => 'إنتاج',
|
||||||
|
'email' => 'البريد الإلكتروني',
|
||||||
|
'phone' => 'رقم الهاتف',
|
||||||
|
'position' => 'المسمى الوظيفي',
|
||||||
|
'notes' => 'ملاحظات',
|
||||||
|
'interactions' => 'التفاعلات',
|
||||||
|
'add_interaction' => 'إضافة تفاعل',
|
||||||
|
'log_interaction' => 'تسجيل تفاعل',
|
||||||
|
'call' => 'مكالمة',
|
||||||
|
'meeting' => 'اجتماع',
|
||||||
|
'applications' => 'الطلبات',
|
||||||
|
'tags' => 'الوسوم',
|
||||||
|
'deadline' => 'الموعد النهائي',
|
||||||
|
'amount' => 'المبلغ',
|
||||||
|
'funding_stage' => 'مرحلة التمويل',
|
||||||
|
'crm_status' => 'حالة CRM',
|
||||||
|
'domain' => 'النطاق',
|
||||||
|
'website' => 'الموقع الإلكتروني',
|
||||||
|
'run' => 'تشغيل',
|
||||||
|
'save_source' => 'حفظ المصدر',
|
||||||
|
'add_source' => 'إضافة مصدر',
|
||||||
|
'telegram_notifications' => 'إشعارات تيليجرام',
|
||||||
|
'telegram_bot_token' => 'رمز البوت',
|
||||||
|
'telegram_chat_id' => 'معرف المحادثة',
|
||||||
|
'telegram_enabled' => 'تفعيل الإشعارات',
|
||||||
|
'test_notification' => 'إرسال تجربة',
|
||||||
|
'language' => 'اللغة',
|
||||||
|
'arabic' => 'العربية',
|
||||||
|
'english' => 'English',
|
||||||
|
];
|
||||||
74
resources/lang/en.php
Normal file
74
resources/lang/en.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dashboard' => 'Dashboard',
|
||||||
|
'organizations' => 'Organizations',
|
||||||
|
'contacts' => 'Contacts',
|
||||||
|
'opportunities' => 'Opportunities',
|
||||||
|
'sources' => 'Data Sources',
|
||||||
|
'settings' => 'Settings',
|
||||||
|
'logout' => 'Logout',
|
||||||
|
'welcome' => 'Welcome',
|
||||||
|
'add' => 'Add',
|
||||||
|
'edit' => 'Edit',
|
||||||
|
'delete' => 'Delete',
|
||||||
|
'save' => 'Save',
|
||||||
|
'cancel' => 'Cancel',
|
||||||
|
'search' => 'Search',
|
||||||
|
'filter' => 'Filter',
|
||||||
|
'name' => 'Name',
|
||||||
|
'type' => 'Type',
|
||||||
|
'status' => 'Status',
|
||||||
|
'country' => 'Country',
|
||||||
|
'city' => 'City',
|
||||||
|
'url' => 'URL',
|
||||||
|
'description' => 'Description',
|
||||||
|
'score' => 'Score',
|
||||||
|
'created' => 'Created',
|
||||||
|
'updated' => 'Updated',
|
||||||
|
'actions' => 'Actions',
|
||||||
|
'no_data' => 'No data',
|
||||||
|
'total_organizations' => 'Total Organizations',
|
||||||
|
'total_opportunities' => 'Total Opportunities',
|
||||||
|
'total_contacts' => 'Total Contacts',
|
||||||
|
'active_sources' => 'Active Sources',
|
||||||
|
'today_opportunities' => "Today's Opportunities",
|
||||||
|
'vc_funds' => 'VC Funds',
|
||||||
|
'accelerators' => 'Accelerators',
|
||||||
|
'recent_opportunities' => 'Recent Opportunities',
|
||||||
|
'recent_activities' => 'Recent Activities',
|
||||||
|
'view_all' => 'View All',
|
||||||
|
'run_collector' => 'Run Collector',
|
||||||
|
'last_collected' => 'Last Collected',
|
||||||
|
'platform_mode' => 'Platform Mode',
|
||||||
|
'local' => 'Local',
|
||||||
|
'production' => 'Production',
|
||||||
|
'email' => 'Email',
|
||||||
|
'phone' => 'Phone',
|
||||||
|
'position' => 'Position',
|
||||||
|
'notes' => 'Notes',
|
||||||
|
'interactions' => 'Interactions',
|
||||||
|
'add_interaction' => 'Add Interaction',
|
||||||
|
'log_interaction' => 'Log Interaction',
|
||||||
|
'call' => 'Call',
|
||||||
|
'meeting' => 'Meeting',
|
||||||
|
'applications' => 'Applications',
|
||||||
|
'tags' => 'Tags',
|
||||||
|
'deadline' => 'Deadline',
|
||||||
|
'amount' => 'Amount',
|
||||||
|
'funding_stage' => 'Funding Stage',
|
||||||
|
'crm_status' => 'CRM Status',
|
||||||
|
'domain' => 'Domain',
|
||||||
|
'website' => 'Website',
|
||||||
|
'run' => 'Run',
|
||||||
|
'save_source' => 'Save Source',
|
||||||
|
'add_source' => 'Add Source',
|
||||||
|
'telegram_notifications' => 'Telegram Notifications',
|
||||||
|
'telegram_bot_token' => 'Bot Token',
|
||||||
|
'telegram_chat_id' => 'Chat ID',
|
||||||
|
'telegram_enabled' => 'Enable Notifications',
|
||||||
|
'test_notification' => 'Send Test',
|
||||||
|
'language' => 'Language',
|
||||||
|
'arabic' => 'العربية',
|
||||||
|
'english' => 'English',
|
||||||
|
];
|
||||||
60
resources/views/admin/settings/index.php
Normal file
60
resources/views/admin/settings/index.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h1><?= $t['settings'] ?? 'Settings' ?></h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($flashSuccess = $this->session->getFlash('success')): ?>
|
||||||
|
<div class="alert alert-success"><span><?= $this->escape($flashSuccess) ?></span></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($flashError = $this->session->getFlash('error')): ?>
|
||||||
|
<div class="alert alert-error"><span><?= $this->escape($flashError) ?></span></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="detail-grid">
|
||||||
|
<!-- Telegram Settings -->
|
||||||
|
<div class="glass-panel">
|
||||||
|
<h3 style="margin-bottom: 20px;">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle; margin-right: 8px; color: #229ED9;"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
|
||||||
|
<?= $t['telegram_notifications'] ?? 'Telegram Notifications' ?>
|
||||||
|
</h3>
|
||||||
|
<form action="/admin/settings/save" method="POST" class="form-stacked">
|
||||||
|
<input type="hidden" name="_csrf" value="<?= $this->session->getCsrfToken() ?>">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><?= $t['telegram_bot_token'] ?? 'Bot Token' ?></label>
|
||||||
|
<input type="text" name="telegram_bot_token" class="form-control" value="<?= $this->escape($tg_token) ?>" placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><?= $t['telegram_chat_id'] ?? 'Chat ID' ?></label>
|
||||||
|
<input type="text" name="telegram_chat_id" class="form-control" value="<?= $this->escape($tg_chat_id) ?>" placeholder="-1001234567890">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="display: flex; align-items: center; gap: 10px;">
|
||||||
|
<label class="form-label" style="margin: 0;"><?= $t['telegram_enabled'] ?? 'Enable' ?></label>
|
||||||
|
<input type="checkbox" name="telegram_enabled" value="1" <?= $tg_enabled === '1' ? 'checked' : '' ?> style="width: 20px; height: 20px;">
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 12px; margin-top: 16px;">
|
||||||
|
<button type="submit" name="action" value="save" class="btn btn-primary"><?= $t['save'] ?? 'Save' ?></button>
|
||||||
|
<button type="submit" name="action" value="test" class="btn btn-secondary"><?= $t['test_notification'] ?? 'Send Test' ?></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Help Info -->
|
||||||
|
<div class="glass-panel">
|
||||||
|
<h3 style="margin-bottom: 16px;">How to set up Telegram</h3>
|
||||||
|
<ol style="line-height: 2; padding-left: 20px; color: var(--text-muted);">
|
||||||
|
<li>Create a bot via <a href="https://t.me/BotFather" target="_blank">@BotFather</a> on Telegram</li>
|
||||||
|
<li>Copy the bot token (e.g., <code>123456:ABC-DEF1234ghIkl</code>)</li>
|
||||||
|
<li>Add the bot to your group/channel</li>
|
||||||
|
<li>Send any message in the group</li>
|
||||||
|
<li>Visit <code>https://api.telegram.org/bot<TOKEN>/getUpdates</code></li>
|
||||||
|
<li>Copy your Chat ID from the response</li>
|
||||||
|
<li>Click "Send Test" to verify</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
|
||||||
|
.form-stacked { display: flex; flex-direction: column; gap: 16px; }
|
||||||
|
@media (max-width: 768px) { .detail-grid { grid-template-columns: 1fr; } }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user