optional wishlist cart exam
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m50s

This commit is contained in:
Ahmed Darrazi 2025-12-18 22:28:00 +01:00
parent 141f4da7e9
commit 52e935b0d0
6 changed files with 133 additions and 35 deletions

View File

@ -26,6 +26,11 @@ class CourseCartController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$system = app('system_settings');
if (($system?->fields['show_course_cart'] ?? true) === false) {
abort(404);
}
$coupon = null; $coupon = null;
$couponCode = $request->input('coupon'); $couponCode = $request->input('coupon');
@ -56,6 +61,11 @@ class CourseCartController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$system = app('system_settings');
if (($system?->fields['show_course_cart'] ?? true) === false) {
abort(404);
}
$this->cartService->addToCart(Auth::user()->id, $request->course_id); $this->cartService->addToCart(Auth::user()->id, $request->course_id);
return redirect(route('course-cart.index'))->with('success', 'Course added to cart successfully.'); return redirect(route('course-cart.index'))->with('success', 'Course added to cart successfully.');
@ -66,6 +76,11 @@ class CourseCartController extends Controller
*/ */
public function update(Request $request, CourseCart $courseCart) public function update(Request $request, CourseCart $courseCart)
{ {
$system = app('system_settings');
if (($system?->fields['show_course_cart'] ?? true) === false) {
abort(404);
}
$this->cartService->clearCart(Auth::user()->id); $this->cartService->clearCart(Auth::user()->id);
return back()->with('success', 'Cart updated successfully.'); return back()->with('success', 'Cart updated successfully.');
@ -76,6 +91,11 @@ class CourseCartController extends Controller
*/ */
public function destroy($course_cart) public function destroy($course_cart)
{ {
$system = app('system_settings');
if (($system?->fields['show_course_cart'] ?? true) === false) {
abort(404);
}
$this->cartService->removeFromCart(Auth::user()->id, $course_cart); $this->cartService->removeFromCart(Auth::user()->id, $course_cart);
return back()->with('success', 'Course removed from cart successfully.'); return back()->with('success', 'Course removed from cart successfully.');

View File

@ -30,6 +30,19 @@ class StudentController extends Controller
*/ */
public function index(Request $request, string $tab) public function index(Request $request, string $tab)
{ {
$system = app('system_settings');
$fields = $system?->fields ?? [];
$showExams = $fields['show_student_exams'] ?? true;
$showWishlist = $fields['show_student_wishlist'] ?? true;
if ($tab === 'exams' && !$showExams) {
abort(404);
}
if ($tab === 'wishlist' && !$showWishlist) {
abort(404);
}
if ($tab !== 'courses' && !$request->user()->hasVerifiedEmail()) { if ($tab !== 'courses' && !$request->user()->hasVerifiedEmail()) {
return redirect() return redirect()
->route('student.index', ['tab' => 'courses']) ->route('student.index', ['tab' => 'courses'])
@ -66,6 +79,13 @@ class StudentController extends Controller
public function show_exam(Request $request, int $id, string $tab) public function show_exam(Request $request, int $id, string $tab)
{ {
$system = app('system_settings');
$fields = $system?->fields ?? [];
if (($fields['show_student_exams'] ?? true) === false) {
abort(404);
}
$user = Auth::user(); $user = Auth::user();
$exam = $this->examEnrollment->getEnrolledExam($id, $user); $exam = $this->examEnrollment->getEnrolledExam($id, $user);
$attempts = $this->examAttempt->getExamAttempts(['exam_id' => $id, 'user_id' => $user->id]); $attempts = $this->examAttempt->getExamAttempts(['exam_id' => $id, 'user_id' => $user->id]);

View File

@ -19,6 +19,7 @@ const Actions = ({ language }: { language: boolean }) => {
const { screen } = useScreen(); const { screen } = useScreen();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const sortedItems = navbar.navbar_items.sort((a, b) => a.sort - b.sort); const sortedItems = navbar.navbar_items.sort((a, b) => a.sort - b.sort);
const showCart = system.fields?.show_course_cart !== false;
const actionElements = () => const actionElements = () =>
sortedItems.map((item) => { sortedItems.map((item) => {
@ -28,7 +29,7 @@ const Actions = ({ language }: { language: boolean }) => {
return <Language key={item.id} />; return <Language key={item.id} />;
} else if (isLoggedIn && item.slug === 'notification') { } else if (isLoggedIn && item.slug === 'notification') {
return <Notification key={item.id} />; return <Notification key={item.id} />;
} else if (isLoggedIn && item.slug === 'cart') { } else if (isLoggedIn && item.slug === 'cart' && showCart) {
return <CourseCart key={item.id} />; return <CourseCart key={item.id} />;
} else { } else {
return null; return null;

View File

@ -6,6 +6,7 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import currencies from '@/data/currencies'; import currencies from '@/data/currencies';
import { onHandleChange } from '@/lib/inertia'; import { onHandleChange } from '@/lib/inertia';
import { SharedData } from '@/types/global'; import { SharedData } from '@/types/global';
@ -24,6 +25,12 @@ const Website = () => {
const { translate } = props; const { translate } = props;
const { input, settings } = translate; const { input, settings } = translate;
const featureDefaults = {
show_course_cart: props.system.fields?.show_course_cart ?? true,
show_student_exams: props.system.fields?.show_student_exams ?? true,
show_student_wishlist: props.system.fields?.show_student_wishlist ?? true,
};
const mediaFields: MediaFields = { const mediaFields: MediaFields = {
new_logo_dark: null, new_logo_dark: null,
new_logo_light: null, new_logo_light: null,
@ -32,6 +39,7 @@ const Website = () => {
}; };
const { data, setData, post, errors, processing } = useForm({ const { data, setData, post, errors, processing } = useForm({
...featureDefaults,
...(props.system.fields as SystemFields), ...(props.system.fields as SystemFields),
...(mediaFields as MediaFields), ...(mediaFields as MediaFields),
}); });
@ -271,6 +279,40 @@ const Website = () => {
<InputError message={errors.instructor_revenue} /> <InputError message={errors.instructor_revenue} />
</div> </div>
)} )}
<div className="md:col-span-3 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div className="flex items-center justify-between rounded-lg border p-3">
<div>
<Label className="text-sm font-medium">Show Course Cart</Label>
<p className="text-muted-foreground text-xs">Enable/disable course cart page and icon.</p>
</div>
<Switch id="show_course_cart" checked={!!data.show_course_cart} onCheckedChange={(checked) => setData('show_course_cart', checked)} />
</div>
<div className="flex items-center justify-between rounded-lg border p-3">
<div>
<Label className="text-sm font-medium">Show Student Exams</Label>
<p className="text-muted-foreground text-xs">Toggle visibility of the student exams tab.</p>
</div>
<Switch
id="show_student_exams"
checked={!!data.show_student_exams}
onCheckedChange={(checked) => setData('show_student_exams', checked)}
/>
</div>
<div className="flex items-center justify-between rounded-lg border p-3">
<div>
<Label className="text-sm font-medium">Show Wishlist</Label>
<p className="text-muted-foreground text-xs">Toggle visibility of the student wishlist tab.</p>
</div>
<Switch
id="show_student_wishlist"
checked={!!data.show_student_wishlist}
onCheckedChange={(checked) => setData('show_student_wishlist', checked)}
/>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -16,43 +16,55 @@ const Layout = ({ children, tab }: { children: ReactNode; tab: string }) => {
const { screen } = useScreen(); const { screen } = useScreen();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { props } = usePage<SharedData>(); const { props } = usePage<SharedData>();
const { translate, auth } = props; const { translate, auth, system } = props;
const { button } = translate; const { button } = translate;
const tabs = useMemo( const tabs = useMemo(
() => [ () => {
{ const list = [
id: 'courses', {
name: button.courses, id: 'courses',
slug: 'courses', name: button.courses,
Icon: GraduationCap, slug: 'courses',
}, Icon: GraduationCap,
{ },
id: 'exams', ...(system.fields?.show_student_exams === false
name: 'Exams', ? []
slug: 'exams', : [
Icon: FileQuestion, {
}, id: 'exams',
{ name: 'Exams',
id: 'wishlist', slug: 'exams',
name: button.wishlist, Icon: FileQuestion,
slug: 'wishlist', },
Icon: Heart, ]),
}, ...(system.fields?.show_student_wishlist === false
{ ? []
id: 'profile', : [
name: button.profile, {
slug: 'profile', id: 'wishlist',
Icon: UserCircle, name: button.wishlist,
}, slug: 'wishlist',
{ Icon: Heart,
id: 'settings', },
name: button.settings, ]),
slug: 'settings', {
Icon: SettingsIcon, id: 'profile',
}, name: button.profile,
], slug: 'profile',
[], Icon: UserCircle,
},
{
id: 'settings',
name: button.settings,
slug: 'settings',
Icon: SettingsIcon,
},
];
return list;
},
[button.courses, button.wishlist, button.profile, button.settings, system.fields?.show_student_exams, system.fields?.show_student_wishlist],
); );
const { post, processing } = useForm({}); const { post, processing } = useForm({});

View File

@ -36,6 +36,9 @@ interface SystemFields {
direction: string; direction: string;
language_selector: boolean; language_selector: boolean;
theme: Appearance; theme: Appearance;
show_course_cart?: boolean;
show_student_exams?: boolean;
show_student_wishlist?: boolean;
} }
interface GoogleAuthFields { interface GoogleAuthFields {