222 lines
4.7 KiB
Vue
222 lines
4.7 KiB
Vue
<template>
|
|
<div class="note-list" :class="{ 'note-list--list': viewMode === 'list' }">
|
|
<div v-if="notes.length === 0" class="empty-notes-state" role="status" aria-live="polite">
|
|
<i class="mdi mdi-file-document-outline empty-notes-icon" aria-hidden="true"></i>
|
|
<h3 class="empty-notes-title">No Notes Yet</h3>
|
|
<p class="empty-notes-message">This space is empty for now. Create your first note to get started.</p>
|
|
</div>
|
|
|
|
<div v-for="note in notes" :key="note.id" class="note-card" :class="{ 'is-pinned': note.is_pinned, 'is-featured': note.is_favorite || note.is_featured }" @click="selectNote(note)">
|
|
<h5 class="note-title">
|
|
<i v-if="note.is_pinned" class="mdi mdi-pin pin-icon" aria-hidden="true"></i>
|
|
<i v-else-if="note.is_favorite || note.is_featured" class="mdi mdi-star featured-icon" aria-hidden="true"></i>
|
|
{{ note.title }}
|
|
</h5>
|
|
<p class="note-preview">{{ getDescription(note) }}</p>
|
|
<small class="text-muted">Updated: {{ formatDate(note.updated_at) }}</small>
|
|
</div>
|
|
|
|
<div v-if="canLoadMore" class="list-footer">
|
|
<button class="btn btn-outline-secondary" :disabled="isLoadingMore" @click="emit('loadMore')">
|
|
{{ isLoadingMore ? "Loading..." : "Load more" }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
defineProps({
|
|
notes: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
canLoadMore: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
isLoadingMore: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
viewMode: {
|
|
type: String,
|
|
default: "grid",
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["selectNote", "loadMore"]);
|
|
|
|
const selectNote = (note) => {
|
|
emit("selectNote", note);
|
|
};
|
|
|
|
const formatDate = (dateString) => {
|
|
return new Date(dateString).toLocaleDateString();
|
|
};
|
|
|
|
const getDescription = (note) => {
|
|
const description = (note?.description || "").trim();
|
|
if (!description) {
|
|
return "No description";
|
|
}
|
|
return description;
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.note-list {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.empty-notes-state {
|
|
grid-column: 1 / -1;
|
|
min-height: 48vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
border: 1px dashed #cfd6e4;
|
|
border-radius: 14px;
|
|
background: linear-gradient(180deg, #f8f9fc 0%, #eef3fb 100%);
|
|
padding: 2rem 1.5rem;
|
|
}
|
|
|
|
.empty-notes-icon {
|
|
font-size: 5.25rem;
|
|
line-height: 1;
|
|
color: #60789a;
|
|
margin-bottom: 0.85rem;
|
|
}
|
|
|
|
.empty-notes-title {
|
|
margin: 0;
|
|
color: #23364f;
|
|
font-size: 1.8rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.empty-notes-message {
|
|
margin: 0.75rem 0 0;
|
|
max-width: 460px;
|
|
color: #4f637d;
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
.note-card {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.note-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.note-title {
|
|
margin-bottom: 0.5rem;
|
|
color: #333;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.pin-icon {
|
|
color: #408aca;
|
|
font-size: 0.9em;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.featured-icon {
|
|
color: #f08c00;
|
|
font-size: 0.95em;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.note-card.is-pinned {
|
|
background: #dbf5ff;
|
|
border-color: #a8d1ff;
|
|
}
|
|
|
|
.note-card.is-featured {
|
|
border-color: #ffd8a8;
|
|
background: #fff9db;
|
|
}
|
|
|
|
.note-preview {
|
|
color: #666;
|
|
margin-bottom: 0.5rem;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.list-footer {
|
|
grid-column: 1 / -1;
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* List view overrides */
|
|
.note-list--list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.note-list--list .note-card {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
padding: 0.6rem 1rem;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.note-list--list .note-card:hover {
|
|
transform: none;
|
|
}
|
|
|
|
.note-list--list .note-title {
|
|
flex: 0 0 220px;
|
|
margin-bottom: 0;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.note-list--list .note-preview {
|
|
flex: 1;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.note-list--list .note-card > small {
|
|
flex: 0 0 auto;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.note-list--list .list-footer {
|
|
grid-column: unset;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.empty-notes-state {
|
|
min-height: 40vh;
|
|
padding: 1.5rem 1rem;
|
|
}
|
|
|
|
.empty-notes-icon {
|
|
font-size: 4.3rem;
|
|
}
|
|
|
|
.empty-notes-title {
|
|
font-size: 1.45rem;
|
|
}
|
|
}
|
|
</style>
|