Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cf6e76e
feat(ui): upgrade mobile navigation to standard slide-over drawer
trtajim Aug 27, 2026
cea4b6e
feat(ui): relocate theme preference segmented control into mobile drawer
trtajim Aug 27, 2026
2c85c48
feat(ui): streamline navigation drawer items and add minimal profile …
trtajim Aug 27, 2026
0c81969
feat(ui): add confirmation dialog before signing out from profile page
trtajim Aug 27, 2026
640cd83
feat(ui): dynamic minimal footer for content/profile pages and full f…
trtajim Aug 27, 2026
d2b7c6a
feat(ui): refine desktop full footer and minimal mobile footer
trtajim Aug 27, 2026
bdff338
style(ui): add top whitespace padding above logo in mobile footer
trtajim Aug 27, 2026
a623ef6
feat(ui): remove verified badge from written by section in blog show …
trtajim Aug 27, 2026
072d7b0
fix(ui): separate author name and view more articles link into distin…
trtajim Aug 27, 2026
21cb2f3
feat(ui): make entire blog card clickable including whitespace
trtajim Aug 27, 2026
76a9427
style(ui): reduce gap between blog cards on mobile screens
trtajim Aug 27, 2026
d59ac1e
feat(ui): clean blog card metadata, remove date and replace read text…
trtajim Aug 27, 2026
ceb04b0
feat(ui): relocate blog card action arrow to bottom right footer
trtajim Aug 27, 2026
4b34bf8
style(ui): add 'By' prefix before author name in BlogCard
trtajim Aug 27, 2026
6e6de70
feat(ui): restore direct login CTA button on mobile navbar when unaut…
trtajim Aug 27, 2026
c25acb2
feat(ui): place 'See all articles' at bottom of blogs section on mobile
trtajim Aug 27, 2026
17db406
style(ui): use minimal text link for mobile 'See all articles'
trtajim Aug 27, 2026
821ddc8
feat(ui): update featured blogs subtitle with linked contributor text…
trtajim Aug 27, 2026
850e824
style(ui): use standard neutral styling for author link in BlogCard
trtajim Aug 27, 2026
8360838
style(ui): reduce gap between subject grid and featured blogs section
trtajim Aug 27, 2026
3c0bd97
style(ui): adjust balanced spacing above featured blogs section
trtajim Aug 27, 2026
4c1d6cc
style(ui): use neutral styling for heart reaction icon in BlogCard
trtajim Aug 27, 2026
6178819
feat(admin): remove 'Internal Dashboard' label from admin sidebars
trtajim Aug 27, 2026
f661ae0
feat(admin): translate admin dashboard to natural Bangla with standar…
trtajim Aug 27, 2026
f45dfcd
feat(admin): remove PostHog references from dashboard texts
trtajim Aug 27, 2026
10ec4b3
feat(admin): update unique visitors label to use natural 'ইউনিক ভিজিট…
trtajim Aug 27, 2026
e4fbbe3
feat(admin): remove scrollbar from top traffic sources list
trtajim Aug 27, 2026
d7427be
feat(ui): remove redundant 'Back to Home' links from static and publi…
trtajim Aug 27, 2026
803b476
feat(docs): hide 'Become an Official Contributor' CTA when user is lo…
trtajim Aug 27, 2026
3a37d6d
feat(ui): rename 'Staff Dashboard' to 'Staff Panel'
trtajim Aug 27, 2026
3c1e08c
feat(ui): simplify navbar by removing 'Projects' and 'About' links
trtajim Aug 27, 2026
ab6c58f
style(ui): restore original theme button and user pill styling in des…
trtajim Aug 27, 2026
dd7a095
feat(ui): remove sign out button from desktop user dropdown
trtajim Aug 27, 2026
ae7e785
feat(repo): remember user course repository selection via cookie and …
trtajim Aug 27, 2026
1cae590
feat(ui): optimize home header whitespace and add collapsible mobile …
trtajim Aug 27, 2026
812d248
refactor(auth): create reusable AuthModal component and refactor shar…
trtajim Aug 27, 2026
7705333
feat(search): move search to navbar takeover with login guard and glo…
trtajim Aug 27, 2026
6605891
style: fix lint errors and clean up unused imports
trtajim Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion app/Http/Controllers/SubjectController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
use App\Models\Node;
use App\Models\Notice;
use App\Models\Subject;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Inertia\Inertia;

class SubjectController extends Controller
{
public function index($course)
public function index(Request $request, $course)
{
// If visiting root '/' and user explicitly preferred 'ssc', redirect cleanly on server side
if ($request->path() === '/' && $request->cookie('preferred_course') === 'ssc') {
return redirect('/ssc');
}
$subjects = Cache::rememberForever("home_page_subjects_{$course}", function () use ($course) {
return Subject::orderBy('sort_order', 'asc')
->where('course', $course)
Expand Down
3 changes: 3 additions & 0 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->encryptCookies(except: [
'preferred_course',
]);
$middleware->web(append: [
HandleInertiaRequests::class,
AddLinkHeadersForPreloadedAssets::class,
Expand Down
18 changes: 17 additions & 1 deletion resources/js/components/AppLogo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@ import { computed } from 'vue';

const page = usePage();
const isSsc = computed(() => page.url.startsWith('/ssc'));

const logoHref = computed(() => {
if (typeof window !== 'undefined') {
try {
const pref = localStorage.getItem('preferred_course');

if (pref === 'ssc') {
return '/ssc';
}
} catch {
// ignore
}
}

return isSsc.value ? '/ssc' : '/';
});
</script>

<template>
<Link href="/" class="flex items-center gap-2.5">
<Link :href="logoHref" class="flex items-center gap-2.5">
<div
class="flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg bg-slate-900 shadow-sm dark:bg-gray-100"
>
Expand Down
74 changes: 74 additions & 0 deletions resources/js/components/AuthModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { Link, usePage } from '@inertiajs/vue3';
import { X, LogIn } from 'lucide-vue-next';

const modelValue = defineModel<boolean>({ default: false });

defineProps({
title: {
type: String,
default: 'Sign in required',
},
message: {
type: String,
default: 'Please sign in to perform this action.',
},
});

const page = usePage();
</script>

<template>
<Teleport to="body">
<Transition
enter-active-class="transition duration-150 ease-out"
enter-from-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="transition duration-100 ease-in"
leave-from-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div
v-if="modelValue"
class="fixed inset-0 z-[100] flex items-center justify-center bg-slate-900/40 p-4 backdrop-blur-xs dark:bg-black/50"
>
<div
class="relative w-full max-w-xs rounded-2xl border border-slate-200 bg-white p-5 shadow-2xl dark:border-gray-800 dark:bg-gray-900"
>
<button
@click="modelValue = false"
class="absolute top-3.5 right-3.5 rounded-lg p-1 text-slate-400 hover:text-slate-600 dark:text-gray-500 dark:hover:text-gray-300"
>
<X class="h-3.5 w-3.5" />
</button>

<h3
class="text-sm font-bold text-slate-900 dark:text-gray-100"
>
{{ title }}
</h3>
<p class="mt-1 text-xs text-slate-500 dark:text-gray-400">
{{ message }}
</p>

<div class="mt-4 flex items-center gap-2">
<Link
:href="`/login?redirect=${encodeURIComponent(page.url)}`"
@click="modelValue = false"
class="inline-flex flex-1 items-center justify-center gap-1.5 rounded-xl bg-slate-900 py-2 text-xs font-semibold text-white transition hover:bg-slate-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
>
<LogIn class="h-3.5 w-3.5" />
<span>Sign in</span>
</Link>
<button
@click="modelValue = false"
class="rounded-xl border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-600 hover:bg-slate-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-800"
>
Cancel
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
102 changes: 25 additions & 77 deletions resources/js/components/BlogCard.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@
<script setup lang="ts">
import { Link } from '@inertiajs/vue3';
import { ArrowRight, Heart, MessageSquare } from 'lucide-vue-next';
import { computed } from 'vue';

const props = defineProps({
defineProps({
blog: Object,
});

const formattedDate = computed(() => {
if (!props.blog?.created_at) {
return '';
}

const date = new Date(props.blog.created_at);

if (isNaN(date.getTime())) {
return '';
}

return new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
}).format(date);
});
</script>

<template>
<div
class="group flex flex-row items-center overflow-hidden rounded-xl border border-slate-200 bg-white p-3 shadow-xs transition-all duration-300 hover:-translate-y-0.5 hover:shadow-md sm:flex-col sm:p-0 dark:border-gray-700 dark:bg-gray-900 dark:hover:shadow-gray-900/50"
<Link
:href="'/blogs/' + blog.slug"
class="group relative flex flex-row items-center overflow-hidden rounded-xl border border-slate-200 bg-white p-3 shadow-xs transition-all duration-300 hover:-translate-y-0.5 hover:shadow-md sm:flex-col sm:p-0 dark:border-gray-700 dark:bg-gray-900 dark:hover:shadow-gray-900/50"
>
<!-- Featured Image Container -->
<Link
:href="'/blogs/' + blog.slug"
<div
class="relative block h-20 w-20 shrink-0 overflow-hidden rounded-lg bg-slate-100 sm:aspect-[16/9] sm:h-auto sm:w-full sm:rounded-none dark:bg-gray-800"
>
<img
Expand All @@ -47,14 +28,14 @@ const formattedDate = computed(() => {
>
{{ blog.category }}
</div>
</Link>
</div>

<!-- Card Body -->
<div class="flex min-w-0 flex-1 flex-col justify-between pl-3 sm:p-4">
<div>
<!-- News Meta (Author & Date & Mobile Category) -->
<!-- Top Row: Category / Author -->
<div
class="mb-1 flex flex-wrap items-center gap-1.5 text-[11px] text-slate-500 sm:mb-1.5 sm:text-xs dark:text-gray-400"
class="mb-1 flex items-center gap-1.5 text-[11px] sm:mb-1.5 sm:text-xs"
>
<span
v-if="blog.category"
Expand All @@ -63,46 +44,29 @@ const formattedDate = computed(() => {
{{ blog.category }}
</span>

<span class="text-slate-400 dark:text-gray-500"
>Author</span
>
<span class="text-slate-400 dark:text-gray-500">By</span>
<Link
v-if="blog.user?.username"
:href="`/u/${blog.user.username}`"
class="font-medium text-indigo-600 underline transition-colors hover:underline dark:text-indigo-400"
@click.stop
class="relative z-10 truncate font-semibold text-slate-700 transition-colors hover:text-indigo-600 hover:underline dark:text-gray-300 dark:hover:text-indigo-400"
>
{{ blog.user?.name }}
</Link>
<span
v-else
class="font-medium text-slate-700 dark:text-gray-300"
class="truncate font-semibold text-slate-700 dark:text-gray-300"
>
{{ blog.user?.name }}
</span>

<span
v-if="formattedDate"
class="text-slate-300 dark:text-gray-600"
>•</span
>

<time
v-if="formattedDate"
:datetime="blog.created_at"
class="text-slate-400 dark:text-gray-500"
>
{{ formattedDate }}
</time>
</div>

<!-- Title -->
<Link :href="'/blogs/' + blog.slug" class="group/title block">
<h3
class="line-clamp-2 text-xs leading-snug font-bold text-slate-900 transition duration-150 group-hover/title:text-indigo-600 sm:text-base dark:text-gray-100 dark:group-hover/title:text-indigo-400"
>
{{ blog.title }}
</h3>
</Link>
<h3
class="line-clamp-2 text-xs leading-snug font-bold text-slate-900 transition duration-150 group-hover:text-indigo-600 sm:text-base dark:text-gray-100 dark:group-hover:text-indigo-400"
>
{{ blog.title }}
</h3>

<!-- Excerpt (hidden on mobile to save vertical space) -->
<p
Expand All @@ -119,21 +83,8 @@ const formattedDate = computed(() => {
<div
class="flex items-center gap-3 text-[11px] text-slate-400 sm:text-xs dark:text-gray-500"
>
<span
class="inline-flex items-center gap-1"
:class="{
'font-medium text-rose-500 dark:text-rose-400':
blog.reactions_count > 0,
}"
>
<Heart
class="h-3.5 w-3.5"
:class="
blog.reactions_count > 0
? 'fill-rose-500 text-rose-500'
: 'text-slate-400 dark:text-gray-500'
"
/>
<span class="inline-flex items-center gap-1">
<Heart class="h-3.5 w-3.5" />
<span>{{ blog.reactions_count || 0 }}</span>
</span>

Expand All @@ -143,16 +94,13 @@ const formattedDate = computed(() => {
</span>
</div>

<Link
:href="'/blogs/' + blog.slug"
class="inline-flex items-center gap-1 text-[11px] font-semibold text-indigo-600 transition hover:text-indigo-700 sm:text-xs dark:text-indigo-400 dark:hover:text-indigo-300"
<!-- Bottom-Right Arrow Indicator -->
<div
class="flex h-5 w-5 shrink-0 items-center justify-center text-slate-400 transition-transform duration-200 group-hover:translate-x-1 group-hover:text-indigo-600 dark:text-gray-500 dark:group-hover:text-indigo-400"
>
<span>Read</span>
<ArrowRight
class="h-3.5 w-3.5 transform transition-transform duration-200 group-hover:translate-x-1"
/>
</Link>
<ArrowRight class="h-3.5 w-3.5" />
</div>
</div>
</div>
</div>
</Link>
</template>
Loading
Loading