Kaynağa Gözat

config view

tum 1 yıl önce
ebeveyn
işleme
3a2749b824

+ 1 - 0
app/coi/settings.py

@@ -41,6 +41,7 @@ INSTALLED_APPS = [
41 41
     'tailwind',
42 42
     'crispy_forms',
43 43
     'crispy_tailwind',
44
+    'django_extensions',
44 45
     'theme.apps.ThemeConfig',
45 46
     'django_browser_reload',
46 47
     "django_filters",

+ 0 - 2
app/core/admin.py

@@ -1,5 +1,3 @@
1
-from django.contrib import admin
2
-
3 1
 # Register your models here.
4 2
 from django.contrib import admin
5 3
 from .models import Report

+ 27 - 1
app/legacy/filters.py

@@ -1,5 +1,5 @@
1 1
 import django_filters
2
-from .models import Data
2
+from .models import Data, DataMs, TbFgPressinfoLotlist, LotSummary
3 3
 
4 4
 class DataFilter(django_filters.FilterSet):
5 5
     lot_no = django_filters.CharFilter(field_name='lot_no', lookup_expr='icontains')
@@ -8,3 +8,29 @@ class DataFilter(django_filters.FilterSet):
8 8
     class Meta:
9 9
         model = Data
10 10
         fields = ['lot_no', 'code']  # Add fields you want to filter
11
+
12
+class DataMsFilter(django_filters.FilterSet):
13
+    lot_no = django_filters.CharFilter(field_name='lot_no', lookup_expr='icontains')
14
+    code = django_filters.CharFilter(field_name='code', lookup_expr='icontains')
15
+
16
+    class Meta:
17
+        model = DataMs
18
+        fields = ['lot_no', 'code']  # Add fields you want to filter
19
+
20
+class TbFgPressFilter(django_filters.FilterSet):
21
+    productcode = django_filters.CharFilter(field_name='productcode', lookup_expr='icontains')
22
+    product_year = django_filters.CharFilter(field_name='product_year', lookup_expr='icontains')
23
+
24
+    class Meta:
25
+        model = TbFgPressinfoLotlist
26
+        fields = ['productcode', 'product_year']  # Add fields you want to filter
27
+
28
+
29
+
30
+class LotSummaryFilter(django_filters.FilterSet):
31
+    lot_no = django_filters.CharFilter(field_name='lot_no', lookup_expr='icontains')
32
+    code = django_filters.CharFilter(field_name='code', lookup_expr='icontains')
33
+
34
+    class Meta:
35
+        model = LotSummary
36
+        fields = ['lot_no', 'code']  # Add fields you want to filter

+ 6 - 3
app/legacy/models.py

@@ -213,6 +213,9 @@ class Data25113027G(models.Model):
213 213
 
214 214
 
215 215
 class DataMs(models.Model):
216
+    lot_no = models.CharField(max_length=50, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)
217
+    machine_id = models.IntegerField()
218
+    code = models.CharField(max_length=50, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)
216 219
     row_no = models.IntegerField()
217 220
     dsize = models.FloatField(blank=True, null=True)
218 221
     tsize = models.FloatField(blank=True, null=True)
@@ -258,9 +261,6 @@ class DataMs(models.Model):
258 261
     ssizeok = models.CharField(db_column='ssizeOk', max_length=4, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)  # Field name made lowercase.
259 262
     created_at = models.DateTimeField(blank=True, null=True)
260 263
     updated_at = models.DateTimeField(blank=True, null=True)
261
-    lot_no = models.CharField(max_length=50, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)
262
-    machine_id = models.IntegerField()
263
-    code = models.CharField(max_length=50, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)
264 264
     emp_id = models.IntegerField()
265 265
     weight = models.FloatField(blank=True, null=True)
266 266
     devid = models.CharField(max_length=40, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True, null=True)
@@ -324,6 +324,9 @@ class DataMs(models.Model):
324 324
         db_table = 'data_ms'
325 325
         app_label = "legacy"
326 326
 
327
+    def __str__(self):
328
+        return f"Lotno:{self.lot_no} Code:{self.code}"
329
+
327 330
 class DataMsLot2210062522(models.Model):
328 331
     row_no = models.IntegerField()
329 332
     dsize = models.FloatField(blank=True, null=True)

+ 29 - 0
app/legacy/templates/legacy/datacrud_confirm_delete.html

@@ -0,0 +1,29 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}Confirm Delete{% endblock %}
4
+
5
+{% block content %}
6
+<div class="container mx-auto px-4 py-6">
7
+    <h1 class="text-2xl font-bold text-gray-800 mb-4">Confirm Deletion</h1>
8
+    <p class="text-gray-600 mb-4">
9
+        Are you sure you want to delete this item?
10
+    </p>
11
+
12
+    <!-- Display object details if needed -->
13
+    <div class="mb-4 p-4 bg-gray-100 rounded border border-gray-200">
14
+        <p><strong>ID:</strong> {{ object.id }}</p>
15
+        <p><strong>Details:</strong> {{ object }}</p>
16
+    </div>
17
+
18
+    <!-- Confirmation Buttons -->
19
+    <form method="post" class="inline-block" onsubmit="return confirm('Are you sure you want to delete this item?');">
20
+        {% csrf_token %}
21
+        <button type="submit" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600">
22
+            Delete
23
+        </button>
24
+    </form>
25
+    <a href="{% url list_url_name %}" class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
26
+        Cancel
27
+    </a>
28
+</div>
29
+{% endblock %}

+ 43 - 0
app/legacy/templates/legacy/datacrud_form.html

@@ -0,0 +1,43 @@
1
+{% extends "base.html" %}
2
+
3
+{% load legacy_filters %}
4
+{% load tailwind_filters %}
5
+
6
+{% block title %}
7
+    {% if view.title %}
8
+        {{ view.title }}
9
+    {% else %}
10
+        {{ view|class_name }}
11
+    {% endif %}
12
+{% endblock %}
13
+
14
+{% block content %}
15
+<div class="container mx-auto px-4 py-6">
16
+    <h1 class="text-2xl font-bold mb-6">
17
+        {% if view.title %}
18
+            {{ view.title }}
19
+        {% elif view|class_name == "CreateViewClass" %}
20
+            Create {{ model_verbose_name }}
21
+        {% else %}
22
+            Update {{ model_verbose_name }}
23
+        {% endif %}
24
+    </h1>
25
+
26
+    <!-- Render the Form -->
27
+    <form method="post" >
28
+        {% csrf_token %}
29
+        <div  class="flex flex-wrap items-center space-x-4">
30
+          
31
+        {{ form|crispy }}
32
+        </div>
33
+        <div class='mt-4'>
34
+            <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
35
+                Save
36
+            </button>
37
+            <a href="{% url list_url_name %}" class="bg-gray-300 text-gray-800 px-4 py-2 rounded hover:bg-gray-400">
38
+                Cancel
39
+            </a>
40
+        </div>
41
+    </form>
42
+</div>
43
+{% endblock %}

+ 91 - 0
app/legacy/templates/legacy/datacrud_list.html

@@ -0,0 +1,91 @@
1
+{% extends "base.html" %}
2
+{% load legacy_filters %}
3
+{% load tailwind_filters %}
4
+{% block title %}{{ page_title }}{% endblock %}
5
+
6
+{% block content %}
7
+<div class="container mx-auto px-4 py-6">
8
+    <h1 class="text-3xl font-bold text-gray-800 mb-4">{{ page_title }}</h1>
9
+
10
+    <!-- Filter Form -->
11
+    <form method="get" class="flex items-center space-x-4 mb-4">
12
+        {{ filter.form | crispy }}
13
+        <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Filter</button>
14
+        <a href="?" class="bg-gray-300 text-gray-800 px-4 py-2 rounded hover:bg-gray-400">Reset</a>
15
+    </form>
16
+
17
+    <!-- Create Button -->
18
+    <div class="my-4 flex">
19
+        <p class="text-gray-600 mb-4 mr-auto">
20
+            Total Records: {{ page_obj.paginator.count }}
21
+        </p>
22
+        <a href="{% url create_url %}" class="ml-auto bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
23
+            Create New Data
24
+        </a>
25
+    </div>
26
+
27
+    <!-- Data Table -->
28
+    <div class="clear-both bg-white shadow rounded-lg overflow-x-auto">
29
+        <table class="w-full border-collapse border border-gray-200 table-fixed">
30
+            <thead>
31
+                <tr class="bg-gray-100 text-left text-sm uppercase">
32
+                    {% for field in fields %}
33
+                      <th class="border border-gray-200 px-4 py-2 text-left">{{ field.verbose_name }}</th>
34
+                    {% endfor %}
35
+                    <th class="py-2 px-4 border-b">Actions</th>
36
+                </tr>
37
+            </thead>
38
+            <tbody>
39
+                {% for obj in page_obj %}
40
+                    <tr class="hover:bg-gray-50">
41
+                        {% for field in fields %}
42
+                        <td class="border border-gray-200 px-4 py-2 whitespace-nowrap">
43
+                           {% if field.name == 'id' %}
44
+                              <a href="{% url  update_url obj.pk %}" class="text-blue-500 hover:underline">
45
+                                  {{ obj|attr:field.name }}
46
+                              </a>
47
+                          {% else %}
48
+                             {{ obj|attr:field.name}}
49
+                          {% endif %}
50
+                        </td>
51
+                        {% endfor %}
52
+                        <td class="py-2 px-4 border-b whitespace-nowrap">
53
+                            <a href="{% url  update_url obj.pk %}" 
54
+                              class="bg-blue-500 text-white px-3 py-2 rounded hover:bg-blue-600">Edit</a>
55
+                            <a href="{% url delete_url obj.pk %}" 
56
+                              class="bg-red-500 text-white px-3 py-2 rounded hover:bg-red-600">Delete</a>
57
+                        </td>
58
+                    </tr>
59
+                {% empty %}
60
+                    <tr>
61
+                        <td colspan="5" class="py-4 px-4 text-center text-gray-600">No data available.</td>
62
+                    </tr>
63
+                {% endfor %}
64
+            </tbody>
65
+        </table>
66
+    </div>
67
+
68
+    <!-- Pagination -->
69
+    <div class="mt-6 flex justify-between items-center">
70
+        <div>
71
+            <span class="text-sm text-gray-600">
72
+                Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
73
+            </span>
74
+        </div>
75
+        <div class="space-x-2">
76
+            {% if page_obj.has_previous %}
77
+                <a href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value|urlencode }}&{% endif %}{% endfor %}page=1" 
78
+                   class="text-blue-500 hover:underline">First</a>
79
+                <a href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value|urlencode }}&{% endif %}{% endfor %}page={{ page_obj.previous_page_number }}" 
80
+                   class="text-blue-500 hover:underline">Previous</a>
81
+            {% endif %}
82
+            {% if page_obj.has_next %}
83
+                <a href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value|urlencode }}&{% endif %}{% endfor %}page={{ page_obj.next_page_number }}" 
84
+                   class="text-blue-500 hover:underline">Next</a>
85
+                <a href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value|urlencode }}&{% endif %}{% endfor %}page={{ page_obj.paginator.num_pages }}" 
86
+                   class="text-blue-500 hover:underline">Last</a>
87
+            {% endif %}
88
+        </div>
89
+    </div>
90
+</div>
91
+{% endblock %}

+ 22 - 1
app/legacy/urls.py

@@ -1,12 +1,33 @@
1 1
 from django.urls import path
2
-from .views import DataListView, DataDetailView, DataCreateView, DataUpdateView, DataDeleteView
2
+from .views import DataListView, DataDetailView, DataCreateView, DataUpdateView, DataDeleteView,\
3
+DataMsCRUDView, TbFgPressInfoLotListCRUDView, LotSummaryCRUDView
3 4
 
4 5
 app_name = 'legacy'  # Namespace for this app
5 6
 
7
+datams_crud = DataMsCRUDView()
8
+fg_crud = TbFgPressInfoLotListCRUDView()
9
+ls_crud = LotSummaryCRUDView()
10
+
11
+
6 12
 urlpatterns = [
7 13
     path('data/', DataListView.as_view(), name='data-list'),            # data/
8 14
     path('data/<int:pk>/', DataDetailView.as_view(), name='data-detail'),  # data/<pk>/
9 15
     path('data/create/', DataCreateView.as_view(), name='data-create'), # data/create/
10 16
     path('data/<int:pk>/update/', DataUpdateView.as_view(), name='data-update'),  # data/<pk>/update/
11 17
     path('data/<int:pk>/delete/', DataDeleteView.as_view(), name='data-delete'),  # data/<pk>/delete/
18
+    
19
+    path('datams/', datams_crud.get_list_view().as_view(), name='datams-list'),
20
+    path('datams/create/', datams_crud.get_create_view().as_view(), name='datams-create'),
21
+    path('datams/<int:pk>/update/', datams_crud.get_update_view().as_view(), name='datams-update'),
22
+    path('datams/<int:pk>/delete/', datams_crud.get_delete_view().as_view(), name='datams-delete'),
23
+    
24
+    path('fg/', fg_crud.get_list_view().as_view(), name='fg-list'),
25
+    path('fg/create/', fg_crud.get_create_view().as_view(), name='fg-create'),
26
+    path('fg/<int:pk>/update/', fg_crud.get_update_view().as_view(), name='fg-update'),
27
+    path('fg/<int:pk>/delete/', fg_crud.get_delete_view().as_view(), name='fg-delete'),
28
+
29
+    path('ls/', ls_crud.get_list_view().as_view(), name='ls-list'),
30
+    path('ls/create/', ls_crud.get_create_view().as_view(), name='ls-create'),
31
+    path('ls/<int:pk>/update/', ls_crud.get_update_view().as_view(), name='ls-update'),
32
+    path('ls/<int:pk>/delete/', ls_crud.get_delete_view().as_view(), name='ls-delete'),
12 33
 ]

+ 258 - 3
app/legacy/views.py

@@ -11,11 +11,11 @@ from django.views.generic import (
11 11
     DeleteView,
12 12
 )
13 13
 from django.core.paginator import Paginator
14
-from .models import Data
15
-from .filters import DataFilter
14
+from .models import Data, DataMs, TbFgPressinfoLotlist, LotSummary
15
+from .filters import DataFilter, DataMsFilter, TbFgPressFilter, LotSummaryFilter
16 16
 from django.urls import reverse
17 17
 from django.contrib import messages
18
-
18
+from pprint import pprint
19 19
 
20 20
 class DataListView(FilterView, ListView):
21 21
     model = Data
@@ -111,3 +111,258 @@ class DataDeleteView(DeleteView):
111 111
             {'label': self.object, 'url': reverse('legacy:data-update', kwargs={'pk':self.object.pk})},
112 112
         ]
113 113
         return context
114
+
115
+class ConfigurableCRUDView:
116
+    model = None
117
+    list_template_name = None
118
+    detail_template_name = None
119
+    form_template_name = None
120
+    confirm_delete_template_name = None
121
+    fields = '__all__'  # Default to all fields
122
+    paginate_by = 10
123
+    filterset_class = None
124
+    page_title = "Page Title"
125
+    create_url_name = None
126
+    update_url_name = None
127
+    delete_url_name = None
128
+    list_url_name = None
129
+    config_fields = "all"  # "all" or a list of field names to display
130
+    config_field_orders = []  # Fields to display first
131
+    config_excludes = []  # Fields to exclude
132
+
133
+    config_readonly_fields = []  # Fields that should be read-only in update view
134
+
135
+    config_edit_fields = "__all__"  # "all" or a list of field names to display
136
+
137
+
138
+
139
+    def get_fields(self):
140
+        """
141
+        Dynamically generate the fields to be displayed in the list view.
142
+        - Includes all fields if `config_fields` is set to "all".
143
+        - Respects `config_field_orders` and `config_excludes`.
144
+        Returns field instances instead of field names.
145
+        """
146
+        model_fields = {f.name: f for f in self.model._meta.get_fields()}
147
+
148
+        # Filter based on `config_fields` configuration
149
+        fields = []
150
+        if self.config_fields != "all":
151
+            fields = [model_fields[f] for f in self.config_fields if f in model_fields]
152
+        else:
153
+            fields = list(model_fields.values())
154
+        pprint("------------------------")
155
+        pprint(f"fields = {fields}")
156
+
157
+        # Exclude fields specified in `config_excludes`
158
+        fields = [f for f in fields if f.name not in self.config_excludes]
159
+
160
+        # Order fields based on `config_field_orders`
161
+        ordered_fields = [f for f in fields if f.name in self.config_field_orders]
162
+        remaining_fields = [f for f in fields if f.name not in self.config_field_orders]
163
+
164
+        # Reorder fields to match the order specified in `config_field_orders`
165
+        ordered_field_names = set(self.config_field_orders)
166
+        ordered_fields.sort(key=lambda f: self.config_field_orders.index(f.name) if f.name in ordered_field_names else len(ordered_field_names))
167
+
168
+        return ordered_fields + remaining_fields
169
+
170
+
171
+    def get_breadcrumbs(self, view_type, obj=None):
172
+        """
173
+        Generate breadcrumbs dynamically based on the view type.
174
+        :param view_type: The type of view (list, create, update, detail, delete)
175
+        :param obj: The current object (if applicable)
176
+        :return: A list of breadcrumb dictionaries
177
+        """
178
+        breadcrumbs = [
179
+            {'label': 'Dashboard', 'url': reverse('dashboard:index')},
180
+            {'label': self.page_title, 'url': reverse(self.list_url_name)} if self.list_url_name else None,
181
+        ]
182
+
183
+        if view_type == 'create':
184
+            breadcrumbs.append({'label': f'Create {self.model._meta.verbose_name}', 'url': None})
185
+        elif view_type == 'update' and obj:
186
+            breadcrumbs.append({'label': obj, 'url': reverse(self.update_url_name, kwargs={'pk': obj.pk})})
187
+        elif view_type == 'detail' and obj:
188
+            breadcrumbs.append({'label': f'Detail of {obj}', 'url': None})
189
+        elif view_type == 'delete' and obj:
190
+            breadcrumbs.append({'label': f'Delete {obj}', 'url': None})
191
+
192
+        return [b for b in breadcrumbs if b]
193
+
194
+    def get_list_view(self):
195
+        class ListViewClass(FilterView, ListView):
196
+            model = self.model
197
+            template_name = self.list_template_name
198
+            paginate_by = self.paginate_by
199
+            filterset_class = self.filterset_class
200
+
201
+            def get_context_data(inner_self, **kwargs):
202
+                context = super().get_context_data(**kwargs)
203
+                fields = self.get_fields()
204
+                context.update({
205
+                    'fields': [f for f in fields],
206
+
207
+                    # 'fields': [field for field in self.model._meta.get_fields()],
208
+                    'page_title': self.page_title,
209
+                    'list_url': self.list_url_name,
210
+                    'create_url': self.create_url_name,
211
+                    'update_url': self.update_url_name,
212
+                    'delete_url': self.delete_url_name,
213
+                    'bs': self.get_breadcrumbs('list'),
214
+                })
215
+                return context
216
+
217
+        return ListViewClass
218
+
219
+    def get_detail_view(self):
220
+        class DetailViewClass(DetailView):
221
+            model = self.model
222
+            template_name = self.detail_template_name
223
+
224
+            def get_context_data(inner_self, **kwargs):
225
+                context = super().get_context_data(**kwargs)
226
+                context.update({
227
+                    'page_title': self.page_title,
228
+                    'bs': self.get_breadcrumbs('detail'),
229
+                })
230
+                return context
231
+
232
+        return DetailViewClass
233
+
234
+    def get_create_view(self):
235
+        class CreateViewClass(CreateView):
236
+            model = self.model
237
+            template_name = self.form_template_name
238
+            fields = self.config_edit_fields
239
+
240
+            def form_valid(inner_self, form):
241
+                response = super().form_valid(form)
242
+                messages.success(inner_self.request, f"{self.model._meta.verbose_name} created successfully!")
243
+                return response
244
+
245
+            def get_success_url(inner_self):
246
+                return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
247
+
248
+            def get_context_data(inner_self, **kwargs):
249
+                context = super().get_context_data(**kwargs)
250
+                context.update({
251
+                    'list_url_name': self.list_url_name,
252
+                    'page_title': f"Create {self.model._meta.verbose_name}",
253
+                    'bs': self.get_breadcrumbs('update', obj=inner_self.object),
254
+
255
+                })
256
+                return context
257
+
258
+        return CreateViewClass
259
+
260
+    def get_update_view(self):
261
+        class UpdateViewClass(UpdateView):
262
+            model = self.model
263
+            template_name = self.form_template_name
264
+            fields = self.config_edit_fields
265
+
266
+            def form_valid(inner_self, form):
267
+                response = super().form_valid(form)
268
+                messages.success(inner_self.request, f"{self.model._meta.verbose_name} updated successfully!")
269
+                return response
270
+
271
+            def get_form(inner_self, *args, **kwargs):
272
+                """
273
+                Customize the form to make specified fields read-only.
274
+                """
275
+                form = super().get_form(*args, **kwargs)
276
+                for field_name in self.config_readonly_fields:
277
+                    if field_name in form.fields:
278
+                        form.fields[field_name].widget.attrs['readonly'] = True
279
+                        form.fields[field_name].widget.attrs['style'] = 'background-color: #f9f9f9; cursor: not-allowed;'
280
+                        form.fields[field_name].disabled = True  # Optional: Completely disable the field
281
+                return form
282
+
283
+            def get_success_url(inner_self):
284
+                return reverse(self.update_url_name, kwargs={'pk': inner_self.object.pk})
285
+
286
+            def get_context_data(inner_self, **kwargs):
287
+                context = super().get_context_data(**kwargs)
288
+                context.update({
289
+                    'list_url_name': self.list_url_name,
290
+                    'page_title': f"Update {self.model._meta.verbose_name}",
291
+                    'bs': self.get_breadcrumbs('update', obj=inner_self.object),
292
+                })
293
+                return context
294
+
295
+        return UpdateViewClass
296
+
297
+    def get_delete_view(self):
298
+        class DeleteViewClass(DeleteView):
299
+            model = self.model
300
+            template_name = self.confirm_delete_template_name
301
+            success_url = reverse_lazy(self.delete_url_name)
302
+
303
+            def get_context_data(inner_self, **kwargs):
304
+                context = super().get_context_data(**kwargs)
305
+                context.update({
306
+                    'list_url_name': self.list_url_name,
307
+                    'page_title': f"Delete {self.model._meta.verbose_name}",
308
+                    'bs': self.get_breadcrumbs('delete', obj=inner_self.object),
309
+                })
310
+                return context
311
+
312
+        return DeleteViewClass
313
+
314
+class DataMsCRUDView(ConfigurableCRUDView):
315
+    model = DataMs
316
+    list_template_name = 'legacy/datacrud_list.html'
317
+    detail_template_name = 'legacy/datacrud_detail.html'
318
+    form_template_name = 'legacy/datacrud_form.html'
319
+    confirm_delete_template_name = 'legacy/datacrud_confirm_delete.html'
320
+    filterset_class = DataMsFilter
321
+
322
+    page_title = "Data Measurement"
323
+
324
+    # URL name mappings
325
+    list_url_name = 'legacy:datams-list'
326
+    create_url_name = 'legacy:datams-create'
327
+    update_url_name = 'legacy:datams-update'
328
+    delete_url_name = 'legacy:datams-delete'
329
+    excludes = ["splitdata"]
330
+    config_field_orders = ["id", "lot_no", "code"]  # Display these fields first
331
+
332
+class TbFgPressInfoLotListCRUDView(ConfigurableCRUDView):
333
+    model = TbFgPressinfoLotlist
334
+    list_template_name = 'legacy/datacrud_list.html'
335
+    detail_template_name = 'legacy/datacrud_detail.html'
336
+    form_template_name = 'legacy/datacrud_form.html'
337
+    confirm_delete_template_name = 'legacy/datacrud_confirm_delete.html'
338
+    filterset_class = TbFgPressFilter
339
+
340
+    page_title = "Fg Press"
341
+
342
+    # URL name mappings
343
+    list_url_name = 'legacy:fg-list'
344
+    create_url_name = 'legacy:fg-create'
345
+    update_url_name = 'legacy:fg-update'
346
+    delete_url_name = 'legacy:fg-delete'
347
+    config_field_orders = ["productcode", "product_year"]  # Display these fields first
348
+
349
+class LotSummaryCRUDView(ConfigurableCRUDView):
350
+    model = LotSummary
351
+    list_template_name = 'legacy/datacrud_list.html'
352
+    detail_template_name = 'legacy/datacrud_detail.html'
353
+    form_template_name = 'legacy/datacrud_form.html'
354
+    confirm_delete_template_name = 'legacy/datacrud_confirm_delete.html'
355
+    filterset_class = DataMsFilter
356
+
357
+    page_title = "Lot Summary"
358
+
359
+    # URL name mappings
360
+    list_url_name = 'legacy:ls-list'
361
+    create_url_name = 'legacy:ls-create'
362
+    update_url_name = 'legacy:ls-update'
363
+    delete_url_name = 'legacy:ls-delete'
364
+    config_fields = ["id", "lot_no", "code", "avg", "start_time", "end_time", "grade", "created_at"]  # Display these fields first
365
+    config_field_orders = ["id", "lot_no", "code", "avg", "start_time", "end_time", "grade", "created_at"]  # Display these fields first
366
+    config_readonly_fields = ["lot_no"]
367
+    config_edit_fields = ["lot_no", "code"]
368
+    

+ 2 - 0
app/package.json

@@ -1,6 +1,8 @@
1 1
 {
2 2
   "dependencies": {
3 3
     "alpinejs": "^3.14.7",
4
+    "font-awesome": "^4.7.0",
5
+    "heroicons": "^2.2.0",
4 6
     "tailwindcss": "^3.4.17",
5 7
     "underscore": "^1.13.7"
6 8
   }

+ 30 - 0
app/templates/base.html

@@ -13,6 +13,7 @@
13 13
     <!--
14 14
     <script src="https://cdn.tailwindcss.com"></script> -->
15 15
     {% tailwind_css %}
16
+    <link href="{% static "font-awesome/css/font-awesome.css" %}" rel="stylesheet" />
16 17
     <script type="text/javascript" defer src="{% static "alpinejs/dist/cdn.min.js" %}"></script>
17 18
     <link href="https://cdn.jsdelivr.net/npm/flowbite@2.5.2/dist/flowbite.min.css" rel="stylesheet" />
18 19
     <script type="text/javascript"  src="{% static "js/main.js" %}"></script>
@@ -63,6 +64,35 @@
63 64
             <ul class="space-y-2">
64 65
                 <li><a href="/dashboard/" class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white"><span class="ml-3">Dashboard</span></a></li>
65 66
                 <li><a href="/reports/" class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white"><span class="ml-3">Reports</span></a></li>
67
+                 <li>
68
+                    <button type="button" class="flex items-center w-full p-2 text-base text-gray-900 transition duration-75 rounded-lg group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700" aria-controls="dropdown-example" data-collapse-toggle="dropdown-example">
69
+                          <svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
70
+  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 6c0 1.657-3.134 3-7 3S5 7.657 5 6m14 0c0-1.657-3.134-3-7-3S5 4.343 5 6m14 0v6M5 6v6m0 0c0 1.657 3.134 3 7 3s7-1.343 7-3M5 12v6c0 1.657 3.134 3 7 3s7-1.343 7-3v-6"/>
71
+</svg>
72
+
73
+                          <span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">Data Tables</span>
74
+                          <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
75
+                             <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
76
+                          </svg>
77
+                    </button>
78
+                    <ul id="dropdown-example" class="hidden py-2 space-y-2">
79
+                          <li>
80
+                            <a href="{% url "legacy:data-list" %}" class="flex items-center w-full p-2 text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">Data</a>
81
+                          </li>
82
+                          <li>
83
+                            <a href="{% url "legacy:datams-list" %}" class="flex items-center w-full p-2 text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">Measurement</a>
84
+                          </li>
85
+                          <li>
86
+                            <a href="{% url "legacy:fg-list" %}" class="flex items-center w-full p-2 text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">Fg Press Info</a>
87
+                          </li>
88
+                          <li>
89
+                            <a href="{% url "legacy:ls-list" %}" class="flex items-center w-full p-2 text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">Lot Summary</a>
90
+                          </li>
91
+                          <li>
92
+                             <a href="#" class="flex items-center w-full p-2 text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">Invoice</a>
93
+                          </li>
94
+                    </ul>
95
+                 </li>
66 96
                 <li><a href="/settings/" class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white"><span class="ml-3">Settings</span></a></li>
67 97
             </ul>
68 98
         </div>