Skip to content

Commit

Permalink
implement Search App, seeding updates bug fixes and new features.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmm1 committed Apr 28, 2024
1 parent 5b35892 commit 0101638
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 13 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This will take _some_ time to build but i am sure it will be worth it in the end

Please see the /docs for more information and getting started on the project.

## Working Capabilities Version 0.0.5
## Working Capabilities Version 0.0.6

- **Login**: Enables registered users to securely access their accounts with their email and password.

Expand All @@ -31,6 +31,8 @@ Please see the /docs for more information and getting started on the project.
- **Contacts**: Basic Implementation, related to BP and Users

- **Tasks and Activities**: Basic Implementation, related to Users and Opportunities

- **Search**: Search for all Models, including Users, Opportunities, Leads, Contacts, and Activities.
-
- - **Sample Data Import**: Import for the apps via seeds view docs/

Expand Down
2 changes: 1 addition & 1 deletion apps/contact/management/commands/seed_contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self, *args, **kwargs):
self.faker = Faker()

def handle(self, *args, **kwargs):
for i in range(10): # Change the range to the number of instances you want to create
for i in range(20): # Change the range to the number of instances you want to create
contact_data = self.generate_fake_data(Contact)
contact_data['business_partner'] = BusinessPartner.objects.first() if BusinessPartner.objects.exists() else None
contact, created = Contact.objects.get_or_create(
Expand Down
Empty file added apps/search/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions apps/search/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from django.contrib import admin

6 changes: 6 additions & 0 deletions apps/search/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class SearchConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.search'
4 changes: 4 additions & 0 deletions apps/search/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django import forms

class SearchForm(forms.Form):
q = forms.CharField(max_length=255, required=False)
Empty file.
3 changes: 3 additions & 0 deletions apps/search/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
3 changes: 3 additions & 0 deletions apps/search/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
7 changes: 7 additions & 0 deletions apps/search/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path
from . import views

urlpatterns = [
path('', views.search, name='search'),
path('search/', views.search_results, name='search_results'),
]
59 changes: 59 additions & 0 deletions apps/search/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.http import JsonResponse
from django.db.models import Q
from django.apps import apps
from django.shortcuts import render, get_object_or_404
from apps.contact.models import Contact
from apps.business_partner.models import BusinessPartner
from apps.tasks.models import Task
from crm.models import Lead
from crm.models import Opportunity
from .forms import SearchForm

def search(request):
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['q']
contacts = Contact.objects.filter(Q(first_name__icontains=query) | Q(last_name__icontains=query))
business_partners = BusinessPartner.objects.filter(Q(name__icontains=query))
tasks = Task.objects.filter(Q(title__icontains=query))
leads = Lead.objects.filter(Q(name__icontains=query))
opportunities = Opportunity.objects.filter(Q(name__icontains=query))

return render(request, 'apps/search/search_results.html', {
'contacts': contacts,
'business_partners': business_partners,
'tasks': tasks,
'leads': leads,
'opportunities': opportunities,
'form': form
})
else:
return render(request, 'apps/search/search_results.html', {'form': form})

import logging

def autocomplete(request):
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
query = request.GET.get('q', '')
models = request.GET.getlist('models', [])
logger.info(f"Autocomplete request: q={query}, models={models}")
results = []

for model in models:
model_class = apps.get_model(model)
model_data = list(model_class.objects.filter(Q(name__icontains=query)).values('id', 'name'))
results.extend(model_data)

logger.info(f"Autocomplete results: {results}")
return JsonResponse({'results': results})

def search_results(request):
query = request.GET.get('q', '')
model_name = request.GET.get('model', '')
if model_name:
model_class = apps.get_model(model_name)
model_data = list(model_class.objects.filter(Q(name__icontains=query)).values('id', 'name'))
return render(request, 'search_results.html', {model_name.lower(): model_data})
else:
return render(request, 'apps/search/search_results.html', {})
1 change: 1 addition & 0 deletions core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
'apps.business_partner.apps.BusinessPartnerConfig',
'apps.contact.apps.ContactConfig',
'apps.tasks.apps.TasksConfig',
'apps.search.apps.SearchConfig',



Expand Down
1 change: 1 addition & 0 deletions core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
path('', include(('apps.business_partner.urls', 'business_partner'), namespace='business_partner')),
path('', include(('apps.tasks.urls', 'tasks'), namespace='tasks')),
path('', include(('apps.contact.urls', 'contact'), namespace='contact')),
path('search/', include(('apps.search.urls', 'search'), namespace='search')),
)

if settings.DEBUG:
Expand Down
7 changes: 7 additions & 0 deletions docs/VERSION.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@
# 0.0.5 (2024-26-04)
- New UI

# 0.0.5 (2024-28-04)
- Model Improvements
- UI Improvements
- Bug Fixes
- Performance Improvements
- Implement Search App

1 change: 1 addition & 0 deletions static/js/ui/ecosphere.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,4 @@

}));


133 changes: 133 additions & 0 deletions templates/apps/search/search_results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{% extends 'base.html' %}
{% load i18n %}

{% block content %}
<div class="page-header">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<h1>{% trans "Search Results" %}</h1>
</div>
</div>
</div>
</div>

<div class="page-body">
<div class="container-xl">
{% if contacts or business_partners or tasks or leads or opportunities %}
{% if contacts %}
<div class="card">
<div class="card-header">
<h3 class="card-title">{% trans "Contacts" %}</h3>
</div>
<div class="card-body">
<div class="row row-cards">
{% for contact in contacts %}
<div class="col-md-4 col-lg-3">
<div class="card">
<div class="card-body">
<a href="{% url 'contact:contact_detail' contact.id %}">{{ contact.first_name }} {{ contact.last_name }}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}

{% if business_partners %}
<div class="card">
<div class="card-header">
<h3 class="card-title">{% trans "Business Partners" %}</h3>
</div>
<div class="card-body">
<div class="row row-cards">
{% for partner in business_partners %}
<div class="col-md-4 col-lg-3">
<div class="card">
<div class="card-body">
<a href="{% url 'business_partner:businesspartner_detail' partner.id %}">{{ partner.name }}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}

{% if tasks %}
<div class="card">
<div class="card-header">
<h3 class="card-title">{% trans "Tasks" %}</h3>
</div>
<div class="card-body">
<div class="row row-cards">
{% for task in tasks %}
<div class="col-md-4 col-lg-3">
<div class="card">
<div class="card-body">
<a href="{% url 'tasks:task_detail' task.id %}">{{ task.title }}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}

{% if leads %}
<div class="card">
<div class="card-header">
<h3 class="card-title">{% trans "Leads" %}</h3>
</div>
<div class="card-body">
<div class="row row-cards">
{% for lead in leads %}
<div class="col-md-4 col-lg-3">
<div class="card">
<div class="card-body">
<a href="{% url 'crm:lead_detail' lead.id %}">{{ lead.name }}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}

{% if opportunities %}
<div class="card">
<div class="card-header">
<h3 class="card-title">{% trans "Opportunities" %}</h3>
</div>
<div class="card-body">
<div class="row row-cards">
{% for opportunity in opportunities %}
<div class="col-md-4 col-lg-3">
<div class="card">
<div class="card-body">
<a href="{% url 'crm:opportunity_detail' opportunity.id %}">{{ opportunity.name }}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% else %}
<div class="row">
<div class="col-md-6">
<div class="alert alert-warning">
{% trans "No results found." %}
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
27 changes: 16 additions & 11 deletions templates/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,22 @@ <h3 class="card-title">Last updates</h3>
</div>
</li>
</ul>
<div class="my-2 my-md-0 flex-grow-1 flex-md-grow-0 order-first order-md-last">
<form action="./" method="get" autocomplete="off" novalidate>
<div class="input-icon">
<span class="input-icon-addon">
<!-- Download SVG icon from http://tabler-icons.io/i/search -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" /><path d="M21 21l-6 -6" /></svg>
</span>
<input type="text" value="" class="form-control" placeholder="{% trans "Search..." %}" aria-label="{% trans "Search the website" %}">
</div>
</form>
</div>
<div class="my-2 my-md-0 flex-grow-1 flex-md-grow-0 order-first order-md-last">
<form id="search-form" action="{% url 'search:search' %}" method="get" autocomplete="off" novalidate>
<div class="input-group">
<span class="input-group-text">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"></path>
<path d="M21 21l-6 -6"></path>
</svg>
</span>
<input id="search-input" type="text" name="q" value="" class="form-control" placeholder="{% trans "Search..." %}" aria-label="{% trans "Search the website" %}">
<button class="btn btn-outline-secondary" type="submit">{% trans "Search" %}</button>
</div>
</form>
</div>

</div>
</div>
</div>
Expand Down

0 comments on commit 0101638

Please sign in to comment.