Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ yarn-error.log
/.nova
/.vscode
/.zed
sail
sail
/prod_backups
2 changes: 1 addition & 1 deletion app/Http/Controllers/TagFrequencyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function search(string $type, string $query = '')
$prefixed_tags = TagFrequency::where('tag', 'like', $escaped.'%')
->where('type', $type)
->orderByDesc('count')
->limit(30)
->limit(50)
->get();

return response()->json([
Expand Down
635 changes: 635 additions & 0 deletions public/images/LogoTitleDark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@
[x-cloak] {
display: none;
}

html.dark {
--surface-ground: #374151 !important;
--surface-card: #374151 !important;
--surface-overlay: #374151 !important;
--surface-border: #4b5563 !important; /* gray-600 for borders */
}

3 changes: 2 additions & 1 deletion resources/js/Components/ApplicationHeaderLogo.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<template>
<img src="/images/LogoTitle.svg" alt="Computer Science Resources" class="w-min sm:w-64 md:w-72 lg:w-80 xl:w-96" />
<img src="/images/LogoTitle.svg" alt="Computer Science Resources" class="h-10 sm:h-12 w-auto block dark:hidden" />
<img src="/images/LogoTitleDark.svg" alt="Computer Science Resources" class="h-10 sm:h-12 w-auto hidden dark:block" />
</template>
15 changes: 15 additions & 0 deletions resources/js/Components/ClickableHeading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup>
import { Icon } from "@iconify/vue";
import { Link } from "@inertiajs/vue3";

const props = defineProps({
href: { type: String, required: true }
});
</script>

<template>
<Link :href="href" class="group flex items-center gap-1">
<slot />
<Icon icon="mdi:chevron-right" class="text-primary group-hover:translate-x-1 transition-transform duration-200" width="22" height="22" />
</Link>
</template>
9 changes: 5 additions & 4 deletions resources/js/Components/Comments/CommentList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const toggleCollapse = () => {
class="absolute left-0 top-0 bottom-0 flex items-center"
>


<div
v-if="!isCollapsed"
class="relative h-full cursor-pointer"
Expand All @@ -49,17 +50,17 @@ const toggleCollapse = () => {

<!-- Visible line -->
<div
class="w-[1.2px] h-full bg-gray-300 transition-colors duration-200"
:class="{'bg-primary': isNearCollapsing}"
class="w-[1.2px] h-full bg-gray-300 dark:bg-gray-800 transition-colors duration-200"
:class="{'bg-primary': isNearCollapsing, 'dark:bg-primary': isNearCollapsing}"
></div>
</div>

<button
v-else
class="p-1 rounded-full border border-gray-300 bg-white hover:border-gray-500 hover:bg-gray-100 transition-colors duration-200"
class="p-1 rounded-full border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 hover:border-gray-500 dark:hover:border-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
@click="toggleCollapse"
>
<Icon icon="mdi:chevron-right" width="20" height="20" class="text-gray-500 hover:text-blue-500" />
<Icon icon="mdi:chevron-right" width="20" height="20" class="text-gray-500 dark:text-gray-300 hover:text-blue-500 dark:hover:text-blue-400" />
</button>
</div>

Expand Down
6 changes: 3 additions & 3 deletions resources/js/Components/Comments/Commentable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,14 @@ onMounted(() => {
</script>

<template>
<div class="comments-section bg-white rounded-lg p-1">
<div class="comments-section bg-white dark:bg-gray-900 rounded-lg p-1">
<SortByDropdown
v-if="props.hasSortByDropdown && hasOpenedComments"
@change="handleSortChange"
></SortByDropdown>

<!-- Error State -->
<div v-if="error" class="text-red-500 mb-4">{{ error }}</div>
<div v-if="error" class="text-red-500 dark:text-red-400 mb-4">{{ error }}</div>

<!-- Comments List -->
<CommentList :id-to-children="idToChildren" />
Expand All @@ -231,7 +231,7 @@ onMounted(() => {
<button
v-if="canLoadMoreComments && !isLoading"
@click="loadComments"
class="w-full py-2 text-center text-primary hover:bg-background/50 transition-colors"
class="w-full py-2 text-center text-primary dark:text-primaryLight hover:bg-background/50 dark:hover:bg-gray-800/50 transition-colors"
>
View {{ commentsLeft }} Comments
</button>
Expand Down
4 changes: 2 additions & 2 deletions resources/js/Components/Comments/SingleComment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const users = inject("users");

<template>
<div
class="py-4 border-b border-gray-300 flex flex-row gap-4 w-full"
class="py-4 border-b border-gray-300 dark:border-gray-800 flex flex-row gap-4 w-full"
:key="comment.id"
:id="'comment_' + comment.id"
>
Expand All @@ -43,7 +43,7 @@ const users = inject("users");
</div>

<!-- Comment Content -->
<p class="mt-2 text-gray-700 break-words">{{ comment.content }}</p>
<p class="mt-2 text-gray-700 dark:text-gray-100 break-words">{{ comment.content }}</p>

<!-- Actions Form -->
<CommentActionsForm
Expand Down
2 changes: 1 addition & 1 deletion resources/js/Components/Comments/SortUpvotesByDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const emit = defineEmits(['change'])

<template>
<div class="flex items-center gap-2 py-4">
<label class="font-semibold text-gray-700">Sort by:</label>
<label class="font-semibold text-gray-700 dark:text-gray-100">Sort by:</label>
<Select
v-model="selectedSort"
:options="sortOptions"
Expand Down
8 changes: 4 additions & 4 deletions resources/js/Components/EmptyState.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ defineProps({

<template>
<div
class="text-center text-gray-500 py-8 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50"
class="text-center text-gray-500 dark:text-gray-400 py-8 border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-900/60"
>
<Icon
:icon="icon"
class="w-12 h-12 mx-auto mb-4 text-gray-400"
class="w-12 h-12 mx-auto mb-4 text-gray-400 dark:text-gray-500"
/>
<h3 class="text-lg font-semibold text-gray-700">{{ title }}</h3>
<p class="mt-1 text-sm">{{ message }}</p>
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">{{ title }}</h3>
<p class="mt-1 text-sm dark:text-gray-400">{{ message }}</p>
</div>
</template>
2 changes: 1 addition & 1 deletion resources/js/Components/Form/FormSaverChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const props = defineProps({
<template>
<div
v-if="isSaved && hasContent"
class="absolute top-4 right-4 bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm font-medium flex items-center gap-1"
class="absolute top-4 right-4 bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 px-3 py-1 rounded-full text-sm font-medium flex items-center gap-1"
>
<Icon icon="mdi:content-save-outline" />
<span>Saved locally</span>
Expand Down
4 changes: 1 addition & 3 deletions resources/js/Components/Form/PrimeVueFormError.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ function formatError(error) {
variant="simple"
>
<template v-for="error in errors" :key="error">
<p>
{{ formatError(error.toString()) }}
</p>
<p><span class="text-red-600 dark:text-red-400">{{ formatError(error.toString()) }}</span></p>
</template>
</Message>
</template>
35 changes: 16 additions & 19 deletions resources/js/Components/Form/TagSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,19 @@ onBeforeUnmount(() => {

// computed properties
const availableTags = computed(() => {
const query = searchQuery.value.trim().toLowerCase();
// Use a filtered/sanitized query for searching tags
const filteredQuery = sanitizeTag(searchQuery.value);

return allTags.value
.filter((tagName) => {
// don't show already selected tags
if (selectedTags.value.includes(tagName)) return false;

// if no query, show all
if (!query) return true;
if (!filteredQuery) return true;

// filter by query
return tagName.toLowerCase().includes(query);
// filter by filtered query (sanitized)
return tagName.toLowerCase().includes(filteredQuery);
})
.map((tagName) => ({
name: tagName,
Expand Down Expand Up @@ -249,11 +250,11 @@ onMounted(async () => {
<span
v-for="tag in selectedTags"
:key="tag"
class="inline-flex items-center mr-2 my-1 bg-secondary text-primaryDark px-3 py-1 rounded-full text-sm font-medium transition-colors"
class="inline-flex items-center mr-2 my-1 bg-secondary text-primaryDark dark:bg-gray-900 dark:text-white px-3 py-1 rounded-full text-sm font-medium transition-colors"
>
<button
@click="removeTag(tag)"
class="mr-2 text-primaryDark"
class="mr-2 text-primaryDark dark:text-white"
type="button"
>
<Icon :icon="'mdi:close'" />
Expand All @@ -272,7 +273,7 @@ onMounted(async () => {
@focus="showDropdown = true"
@blur="onBlur"
placeholder="Add tags..."
class="w-full px-3 py-2 text-md border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-all"
class="w-full px-3 py-2 text-md border border-gray-400 dark:border-gray-600 rounded-lg focus:outline-none placeholder-gray-400 focus:ring-2 focus:ring-primary focus:border-primary dark:bg-black transition-all"
:class="{
'rounded-b-none border-b-0':
showDropdown &&
Expand All @@ -287,7 +288,7 @@ onMounted(async () => {
showDropdown &&
(availableTags.length > 0 || canCreateNew || isLoading)
"
class="z-[9999] bg-white border border-gray-300 border-t-0 rounded-b-lg shadow-lg max-h-60 overflow-y-auto"
class="z-[9999] bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-800 border-t-0 rounded-b-lg shadow-lg max-h-60 overflow-y-auto"
:style="dropdownStyles"
>
<!-- loading state -->
Expand All @@ -313,18 +314,16 @@ onMounted(async () => {
@mouseenter="highlightedIndex = index"
class="flex items-center justify-between px-3 py-1.5 cursor-pointer transition-colors"
:class="{
'bg-secondary text-primaryDark':
highlightedIndex === index,
'hover:bg-gray-50': highlightedIndex !== index,
'bg-secondary text-primaryDark dark:bg-gray-800 dark:text-primaryLight': highlightedIndex === index,
'hover:bg-gray-50 dark:hover:bg-gray-900': highlightedIndex !== index,
}"
>
<span class="text-sm">{{ tag.name }}</span>
<span
v-if="tag.count"
class="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded-full"
class="text-xs bg-gray-100 text-gray-600 dark:bg-gray-900 dark:text-gray-300 px-2 py-1 rounded-full"
:class="{
'bg-secondary text-primaryDark':
highlightedIndex === index,
'bg-secondary text-primaryDark dark:bg-gray-800 dark:text-primaryLight': highlightedIndex === index,
}"
>
{{ tag.count }}
Expand All @@ -338,17 +337,15 @@ onMounted(async () => {
@mouseenter="highlightedIndex = availableTags.length"
class="flex items-center px-4 py-3 cursor-pointer transition-colors"
:class="{
'bg-secondary text-primaryDark':
highlightedIndex === availableTags.length,
'hover:bg-gray-50':
highlightedIndex !== availableTags.length,
'bg-secondary text-primaryDark dark:bg-gray-800 dark:text-primaryLight': highlightedIndex === availableTags.length,
'hover:bg-gray-50 dark:hover:bg-gray-900': highlightedIndex !== availableTags.length,
}"
>
<Icon
class="w-4 h-4 text-green-500"
:icon="'mdi:add'"
></Icon>
<span class="text-sm text-gray-700">
<span class="text-sm text-gray-700 dark:text-gray-100">
create "<strong>{{
sanitizeTag(searchQuery.trim())
}}</strong
Expand Down
10 changes: 5 additions & 5 deletions resources/js/Components/ListInput.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { ref, watch } from "vue";
import InputText from "primevue/inputtext";
import { Icon } from "@iconify/vue";
import TextInput from "./TextInput.vue";

const emit = defineEmits(["change"]);

Expand All @@ -23,8 +23,8 @@ watch(
() => props.initialValues,
(newInitialValues) => {
// Only update if the content is actually different to avoid unnecessary updates
const currentFiltered = items.value.filter(item => item !== "");
const newFiltered = newInitialValues.filter(item => item !== "");
const currentFiltered = items.value.filter((item) => item !== "");
const newFiltered = newInitialValues.filter((item) => item !== "");

if (JSON.stringify(currentFiltered) !== JSON.stringify(newFiltered)) {
items.value = [...newInitialValues];
Expand Down Expand Up @@ -65,7 +65,7 @@ const updateItem = (index, value) => {
:key="index"
class="flex items-center gap-2"
>
<InputText
<TextInput
:value="item"
@input="(event) => updateItem(index, event.target.value)"
placeholder="Enter an item"
Expand All @@ -82,10 +82,10 @@ const updateItem = (index, value) => {
</div>

<button
v-if="items.length < maxSize"
type="button"
@click="addItem"
class="flex items-center gap-1 text-blue-500 focus:outline-none"
:disabled="items.length >= maxSize"
>
<Icon icon="mdi:plus" />
<span>Add</span>
Expand Down
1 change: 0 additions & 1 deletion resources/js/Components/Navbar/UserDropdown.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup>
import { computed } from 'vue';
import { router } from "@inertiajs/vue3";
import Dropdown from '@/Components/Dropdown.vue';
import DropdownLink from '@/Components/DropdownLink.vue';
Expand Down
Loading