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
18 changes: 15 additions & 3 deletions resources/js/Components/DropdownLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@ defineProps({
href: String,
as: String,
});

const dropdownLinkClasses = "block px-4 py-2 text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out";
</script>

<template>
<Link :href="href" class="block px-4 py-2 text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out">
<slot />
</Link>
<div>
<button v-if="as === 'button'" :class="dropdownLinkClasses" class="w-full text-start">
<slot />
</button>

<a v-else-if="as === 'a'" :class="dropdownLinkClasses" class="w-full text-start" :href="href">
<slot />
</a>

<Link v-else :href="href" :class="dropdownLinkClasses">
<slot />
</Link>
</div>
</template>
2 changes: 1 addition & 1 deletion resources/js/Components/Navbar/UserDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const logout = () => {
<div class="border-t border-gray-200 dark:border-gray-600" />

<!-- Authentication -->
<form @submit.prevent="logout">
<form method="POST" @submit.prevent="logout">
<DropdownLink as="button">
Log Out
</DropdownLink>
Expand Down
17 changes: 11 additions & 6 deletions resources/js/Components/Navigation/BackButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ const props = defineProps({
route: {
type: String,
required: true,
}
})

},
});
</script>

<template>
<Link :href="props.route">
<Icon icon="mdi:arrow-back" class="text-xl my-auto"></Icon>
</Link>
<div>
<Link :href="props.route">
<div class="flex flex-row gap-2">
<Icon icon="mdi:arrow-back" class="text-xl my-auto"></Icon>
<slot />
</div>
</Link>
<div class="mt-2 border-t border-gray-200 dark:border-gray-700"></div>
</div>
</template>
4 changes: 2 additions & 2 deletions resources/js/Components/Navigation/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { ref } from "vue";
import { Link, router } from "@inertiajs/vue3";
import { Icon } from "@iconify/vue";
import UserDropdown from "@/Components/Navbar/UserDropdown.vue";
import NavLink from "@/Components/NavLink.vue";
import NavLinkDropdown from "@/Components/NavLinkDropdown.vue";
// import NavLink from "@/Components/NavLink.vue";
import DropdownLink from "@/Components/DropdownLink.vue";
import ResponsiveNavLink from "@/Components/ResponsiveNavLink.vue";
import SecondaryButton from "@/Components/SecondaryButton.vue";
Expand Down Expand Up @@ -56,7 +56,7 @@ const { isDark, toggleDark } = useDarkMode();

<NavLinkDropdown
label="Resources"
:active="route().current('resources.*')"
:active="route().current('resources.index') || route().current('resource_edits.index')"
>
<DropdownLink :href="route('resources.index')">
<Icon icon="mdi:magnify" class="mr-2" />
Expand Down
88 changes: 88 additions & 0 deletions resources/js/Components/ResourceEdits/ApprovalActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script setup>
import { Icon } from "@iconify/vue";
import Upvotable from "@/Components/Upvote/Upvotable.vue";

defineProps({
editedResource: {
type: Object,
required: true,
},
});
</script>

<template>
<div class="mt-8 border-t border-gray-200 dark:border-gray-800 pt-6">
<div class="flex justify-center">
<Upvotable
:flexRow="true"
:upvotable-key="'edit'"
:upvotable-id="editedResource.id"
:initial-votes="editedResource.vote_score"
:user-vote="editedResource.user_vote"
:refresh="true"
class="flex items-center gap-6"
>
<!-- Downvote (Reject) Button -->
<template #alreadyDownvotedIcon>
<span
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-lg font-medium"
>
<Icon
icon="mdi:close-circle"
class="w-4 h-4 mr-2"
/>
Rejected
</span>
</template>
<template #downvoteIcon>
<span
class="inline-flex items-center px-4 py-2 bg-red-50 hover:bg-red-100 text-red-700 border border-red-200 rounded-lg font-medium transition-colors cursor-pointer"
>
<Icon
icon="mdi:close-circle-outline"
class="w-4 h-4 mr-2"
/>
Reject Changes
</span>
</template>

<!-- Vote Count -->
<template #votes="{ votes }">
<div class="flex flex-col items-center">
<span
class="text-2xl font-bold text-gray-900 dark:text-gray-100"
>{{ votes }}</span
>
<span class="text-sm text-gray-600 dark:text-gray-300">
Approval{{ votes === 1 ? "" : "s" }}
</span>
</div>
</template>

<!-- Upvote (Approve) Button -->
<template #alreadyUpvotedIcon>
<span
class="inline-flex items-center px-4 py-2 bg-green-500 text-white rounded-lg font-medium"
>
<Icon
icon="mdi:check-circle"
class="w-4 h-4 mr-2"
/>
Approved!
</span>
</template>
<template #upvoteIcon>
<span
class="inline-flex items-center px-4 py-2 bg-green-200 hover:bg-green-300 text-green-800 border border-green-300 rounded-lg font-medium transition-colors cursor-pointer"
>
<Icon
icon="mdi:check-circle-outline"
class="w-4 h-4 mr-2"
/>
Approve Changes
</span>
</template>
</Upvotable>
</div>
</div>
</template>
47 changes: 47 additions & 0 deletions resources/js/Components/ResourceEdits/DiffViewTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup>
import { Icon } from "@iconify/vue";

defineProps({
changedFields: {
type: Array,
required: true,
},
hasChanges: {
type: Boolean,
required: true,
},
});
</script>

<template>
<div v-if="hasChanges" class="space-y-6">
<div
v-for="field in changedFields"
:key="field.key"
class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg overflow-hidden"
>
<div
class="bg-gray-100 dark:bg-gray-800 px-4 py-2 border-b border-gray-200 dark:border-gray-800"
>
<h3
class="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2"
>
<Icon
icon="mdi:file-edit"
class="w-4 h-4 text-primary"
/>
{{ field.label }}
</h3>
</div>
<div class="p-4">
<component
:is="field.component"
:field="field"
/>
</div>
</div>
</div>
<p v-else class="text-gray-500 dark:text-gray-400 italic p-4">
No changes to display in diff.
</p>
</template>
54 changes: 54 additions & 0 deletions resources/js/Components/ResourceEdits/FieldDisplay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup>
import Tag from "primevue/tag";
import ResourceThumbnail from "@/Components/Resources/ResourceThumbnail.vue";

defineProps({
field: {
type: Object,
required: true,
},
value: {
required: true,
},
altText: {
type: String,
default: 'Image',
},
});
</script>

<template>
<div>
<h3 class="font-semibold text-gray-600 dark:text-gray-300 mb-2">
{{ field.label }}:
</h3>
<div v-if="field.key === 'image_url'">
<p v-if="!value" class="italic">Image Removed</p>
<ResourceThumbnail
v-else
:src="value"
:alt="altText"
/>
</div>
<div
v-else-if="Array.isArray(value)"
class="flex flex-wrap gap-2"
>
<Tag
v-for="item in value"
:key="item"
:value="field.formatter ? field.formatter(item) : item"
/>
</div>
<div
v-else
class="text-gray-800 dark:text-gray-100 p-2 rounded"
>
{{
field.formatter
? field.formatter(value)
: value
}}
</div>
</div>
</template>
71 changes: 71 additions & 0 deletions resources/js/Components/ResourceEdits/HeaderSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script setup>
import { Icon } from "@iconify/vue";
import BackButton from "@/Components/Navigation/BackButton.vue";
import UserProfile from "@/Components/Profile/UserProfile.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";

defineProps({
editedResource: {
type: Object,
required: true,
},
originalResource: {
type: Object,
required: true,
},
});

const emit = defineEmits(['merge']);
</script>

<template>
<div class="my-2">
<BackButton
:route="
route('resources.show', {
slug: originalResource.slug,
tab: 'edits',
})
"
>
<span class="my-auto">Back to {{ originalResource.name }}</span>
</BackButton>
<div class="border-b border-gray-200 dark:border-gray-800 pb-6 mb-6">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center gap-3 mb-3">
<h1
class="text-3xl font-bold text-gray-900 dark:text-gray-100"
>
{{ editedResource.edit_title }}
</h1>
</div>
<p
class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4"
>
{{ editedResource.edit_description }}
</p>

<UserProfile
:user="editedResource.user"
:date="editedResource.created_at"
/>
</div>
<div
v-if="editedResource.can_merge_edits"
class="ml-6"
>
<PrimaryButton
@click="emit('merge', editedResource.id)"
>
<Icon
icon="mdi:source-merge"
class="w-4 h-4 mr-2"
/>
Merge Changes
</PrimaryButton>
</div>
</div>
</div>
</div>
</template>
Loading