Add standalone search page

This commit is contained in:
2024-09-08 20:34:31 +10:00
parent 2007232aea
commit 10956c32dc
13 changed files with 171 additions and 2 deletions
+50
View File
@@ -74,3 +74,53 @@ Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License.
## Local Development
To run the URL Manager locally:
1. Clone the repository:
```
git clone https://github.com/yourusername/url-manager.git
cd url-manager
```
2. Create and activate a virtual environment:
```
python -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
```
3. Install dependencies:
```
pip install -r requirements.txt
```
4. Apply database migrations:
```
python manage.py migrate
```
5. Create a superuser:
```
python manage.py createsuperuser
```
6. Compile translation messages:
```
python manage.py compilemessages
```
7. In one terminal, start the Tailwind CSS build process:
```
python manage.py tailwind start
```
8. In another terminal, run the Django development server:
```
python manage.py runserver
```
9. Open your browser and go to `http://127.0.0.1:8000`
Remember to keep both the Tailwind CSS build process and the Django development server running while you're developing.
View File
Binary file not shown.
Binary file not shown.
+43
View File
@@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="max-w-4xl mx-auto mt-8">
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<div class="px-6 py-4 bg-gray-50 border-b border-gray-200">
<h1 class="text-2xl font-bold text-gray-800">{% trans "Search Links" %}</h1>
</div>
<div class="p-6">
<form method="get" action="{% url 'search' %}" class="mb-6">
<div class="flex">
<input type="text" name="q" value="{{ query }}" class="flex-grow px-4 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="{% trans 'Enter alias or URL' %}">
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">{% trans "Search" %}</button>
</div>
</form>
{% if query %}
<h2 class="text-xl font-semibold mb-4">{% trans "Search Results for" %} "{{ query }}"</h2>
{% if links %}
<ul class="divide-y divide-gray-200">
{% for link in links %}
<li class="py-4">
<div class="flex justify-between">
<div>
<a href="{% url 'redirect_to_original' link.alias %}" class="text-blue-600 hover:underline font-semibold">{{ link.alias }}</a>
<p class="text-sm text-gray-600">{{ link.original_url }}</p>
</div>
<div class="text-sm text-gray-500">
{% trans "Clicks" %}: {{ link.click_count }}
</div>
</div>
</li>
{% endfor %}
</ul>
{% else %}
<p class="text-gray-600">{% trans "No results found." %}</p>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endblock %}
+1
View File
@@ -8,5 +8,6 @@ urlpatterns = [
path('delete/<int:pk>/', views.LinkDeleteView.as_view(), name='link_delete'),
path('delete-selected/', views.delete_selected, name='delete_selected'),
path('detail/<int:pk>/', views.LinkDetailView.as_view(), name='link_detail'),
path('search/', views.SearchView.as_view(), name='search'),
path('<str:alias>/', views.redirect_to_original, name='redirect_to_original'),
]
+13 -1
View File
@@ -1,5 +1,5 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView, TemplateView
from django.urls import reverse_lazy, reverse # Add 'reverse' here
from django.db.models import F, Count, Q
from django.db.models.functions import TruncDate
@@ -134,3 +134,15 @@ def search_aliases(request):
print(f"Search results: {results}") # Debug print
return JsonResponse(results, safe=False)
class SearchView(TemplateView):
template_name = 'links/search.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
query = self.request.GET.get('q', '')
if query:
links = Link.objects.filter(Q(alias__icontains=query) | Q(original_url__icontains=query))
context['links'] = links
context['query'] = query
return context
Binary file not shown.
+18
View File
@@ -96,6 +96,24 @@ msgstr "编辑链接"
msgid "Search aliases..."
msgstr "搜索别名..."
msgid "Search Links"
msgstr "搜索链接"
msgid "Enter alias or URL"
msgstr "输入别名或URL"
msgid "Search"
msgstr "搜索"
msgid "Search Results for"
msgstr "搜索结果:"
msgid "No results found."
msgstr "未找到结果。"
msgid "Advanced Search"
msgstr "高级搜索"
#: 3.12/lib/python3.12/site-packages/django/forms/fields.py:95
msgid "This field is required."
msgstr ""
+43
View File
@@ -888,6 +888,10 @@ select {
margin-top: 5rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.block {
display: block;
}
@@ -952,6 +956,10 @@ select {
max-width: 56rem;
}
.flex-grow {
flex-grow: 1;
}
.border-collapse {
border-collapse: collapse;
}
@@ -1045,6 +1053,16 @@ select {
border-radius: 0.375rem;
}
.rounded-l-md {
border-top-left-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
}
.rounded-r-md {
border-top-right-radius: 0.375rem;
border-bottom-right-radius: 0.375rem;
}
.border {
border-width: 1px;
}
@@ -1313,22 +1331,47 @@ select {
color: rgb(127 29 29 / var(--tw-text-opacity));
}
.hover\:text-gray-200:hover {
--tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity));
}
.hover\:underline:hover {
text-decoration-line: underline;
}
.focus\:border-blue-300:focus {
--tw-border-opacity: 1;
border-color: rgb(147 197 253 / var(--tw-border-opacity));
}
.focus\:outline-none:focus {
outline: 2px solid transparent;
outline-offset: 2px;
}
.focus\:ring:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-2:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-blue-200:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity));
}
.focus\:ring-blue-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
}
.focus\:ring-opacity-50:focus {
--tw-ring-opacity: 0.5;
}
+1
View File
@@ -35,6 +35,7 @@
<div class="container mx-auto flex justify-between items-center">
<a href="{% url 'link_list' %}" class="text-xl font-bold">{% trans "URL Manager" %}</a>
<div class="flex items-center space-x-4">
<a href="{% url 'search' %}" class="text-white hover:text-gray-200">{% trans "Advanced Search" %}</a>
<div class="relative">
<input type="text" id="search-input" class="bg-white text-gray-800 rounded px-4 py-2 w-64" placeholder="{% trans 'Search aliases...' %}">
<div id="search-results" class="absolute top-full left-0 right-0 bg-white border border-gray-300 rounded mt-1 hidden"></div>
Binary file not shown.
+2 -1
View File
@@ -58,7 +58,8 @@ ALLOWED_HOSTS = []
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/app/data/db.sqlite3', # Updated path
# 'NAME': '/app/data/db.sqlite3', # Updated path
'NAME': BASE_DIR / 'data/db.sqlite3',
}
}