- Bug fixes:
- Catalog Name Unique Constraint - Selection constraint to view processed document - remove tab from tenant overview
This commit is contained in:
@@ -8,7 +8,7 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
class Catalog(db.Model):
|
class Catalog(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
name = db.Column(db.String(50), nullable=False)
|
name = db.Column(db.String(50), nullable=False, unique=True)
|
||||||
description = db.Column(db.Text, nullable=True)
|
description = db.Column(db.Text, nullable=True)
|
||||||
type = db.Column(db.String(50), nullable=False, default="STANDARD_CATALOG")
|
type = db.Column(db.String(50), nullable=False, default="STANDARD_CATALOG")
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,18 @@
|
|||||||
|
|
||||||
{% block content_title %}Document Versions{% endblock %}
|
{% block content_title %}Document Versions{% endblock %}
|
||||||
{% block content_description %}View Versions for {{ document }}{% endblock %}
|
{% block content_description %}View Versions for {{ document }}{% endblock %}
|
||||||
{% block content_class %}<div class="col-xl-12 col-lg-5 col-md-7 mx-auto">{% endblock %}
|
{% block content_class %}<div class="col-xl-12 col-lg-5 col-md-7 mx-auto"></div>{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form method="POST" action="{{ url_for('document_bp.handle_document_version_selection') }}">
|
<form method="POST" action="{{ url_for('document_bp.handle_document_version_selection') }}" id="documentVersionsForm">
|
||||||
{{ render_selectable_table(headers=["ID", "URL", "Object Name", "File Type", "Process.", "Proces. Start", "Proces. Finish", "Proces. Error"], rows=rows, selectable=True, id="versionsTable") }}
|
{{ render_selectable_table(headers=["ID", "URL", "Object Name", "File Type", "Process.", "Proces. Start", "Proces. Finish", "Proces. Error"], rows=rows, selectable=True, id="versionsTable") }}
|
||||||
<div class="form-group mt-3">
|
<div class="form-group mt-3 d-flex justify-content-between">
|
||||||
<button type="submit" name="action" value="edit_document_version" class="btn btn-primary">Edit Document Version</button>
|
<div>
|
||||||
<button type="submit" name="action" value="view_document_version_markdown" class="btn btn-danger">View Processed Document</button>
|
<button type="submit" name="action" value="edit_document_version" class="btn btn-primary" onclick="return validateTableSelection('documentVersionsForm')">Edit Document Version</button>
|
||||||
<button type="submit" name="action" value="process_document_version" class="btn btn-danger">Process Document Version</button>
|
<button type="submit" name="action" value="view_document_version_markdown" class="btn btn-danger" onclick="return validateTableSelection('documentVersionsForm')">View Processed Document</button>
|
||||||
|
<button type="submit" name="action" value="process_document_version" class="btn btn-danger" onclick="return validateTableSelection('documentVersionsForm')">Process Document Version</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<div class="form-group mt-3 d-flex justify-content-between">
|
<div class="form-group mt-3 d-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" name="action" value="edit_document_version" class="btn btn-primary" onclick="return validateTableSelection('documentVersionsForm')">Edit Document Version</button>
|
<button type="submit" name="action" value="edit_document_version" class="btn btn-primary" onclick="return validateTableSelection('documentVersionsForm')">Edit Document Version</button>
|
||||||
<button type="submit" name="action" value="view_document_version_markdown" class="btn btn-danger">View Processed Document</button>
|
<button type="submit" name="action" value="view_document_version_markdown" class="btn btn-danger" onclick="return validateTableSelection('documentVersionsForm')">View Processed Document</button>
|
||||||
<button type="submit" name="action" value="process_document_version" class="btn btn-danger" onclick="return validateTableSelection('documentVersionsForm')">Process Document Version</button>
|
<button type="submit" name="action" value="process_document_version" class="btn btn-danger" onclick="return validateTableSelection('documentVersionsForm')">Process Document Version</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% from "macros.html" import render_field, render_included_field %}
|
{% from "macros.html" import render_field, render_included_field, debug_to_console %}
|
||||||
|
|
||||||
{% block title %}Tenant Overview{% endblock %}
|
{% block title %}Tenant Overview{% endblock %}
|
||||||
|
|
||||||
@@ -9,162 +9,23 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
<!-- Main Tenant Information -->
|
{% set disabled_fields = [] %}
|
||||||
{% set main_fields = ['name', 'code', 'website', 'default_language', 'allowed_languages', 'type'] %}
|
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
{{ render_included_field(field, disabled_fields=main_fields, include_fields=main_fields) }}
|
{{ debug_to_console('field to disable', field.name) }}
|
||||||
|
{{ debug_to_console('field type to disable', field.type) }}
|
||||||
|
{% if field.name != 'csrf_token' and field.type != 'HiddenField' %}
|
||||||
|
{% set disabled_fields = disabled_fields + [field.name] %}
|
||||||
|
{{ debug_to_console('disable', '!') }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ debug_to_console('disabled_fields', disabled_fields) }}
|
||||||
|
{% set exclude_fields = [] %}
|
||||||
|
{% for field in form %}
|
||||||
|
{{ render_field(field, disabled_fields, exclude_fields) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<!-- Nav Tabs -->
|
|
||||||
<div class="row mt-5">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="nav-wrapper position-relative end-0">
|
|
||||||
<ul class="nav nav-pills nav-fill p-1" role="tablist">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link mb-0 px-0 py-1" data-bs-toggle="tab" href="#license-info-tab" role="tab" aria-controls="license-info" aria-selected="false">
|
|
||||||
License Information
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tab-content tab-space">
|
|
||||||
<!-- License Information Tab -->
|
|
||||||
<div class="tab-pane fade" id="license-info-tab" role="tabpanel">
|
|
||||||
{% set license_fields = ['currency', 'usage_email', ] %}
|
|
||||||
{% for field in form %}
|
|
||||||
{{ render_included_field(field, disabled_fields=license_fields, include_fields=license_fields) }}
|
|
||||||
{% endfor %}
|
|
||||||
<!-- Register API Key Button -->
|
|
||||||
<button type="button" class="btn btn-primary" onclick="generateNewChatApiKey()">Register Chat API Key</button>
|
|
||||||
<button type="button" class="btn btn-primary" onclick="generateNewApiKey()">Register API Key</button>
|
|
||||||
<!-- API Key Display Field -->
|
|
||||||
<div id="chat-api-key-field" style="display:none;">
|
|
||||||
<label for="chat-api-key">Chat API Key:</label>
|
|
||||||
<input type="text" id="chat-api-key" class="form-control" readonly>
|
|
||||||
<button type="button" id="copy-chat-button" class="btn btn-primary">Copy to Clipboard</button>
|
|
||||||
<p id="copy-chat-message" style="display:none;color:green;">Chat API key copied to clipboard</p>
|
|
||||||
</div>
|
|
||||||
<div id="api-key-field" style="display:none;">
|
|
||||||
<label for="api-key">API Key:</label>
|
|
||||||
<input type="text" id="api-key" class="form-control" readonly>
|
|
||||||
<button type="button" id="copy-api-button" class="btn btn-primary">Copy to Clipboard</button>
|
|
||||||
<p id="copy-message" style="display:none;color:green;">API key copied to clipboard</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content_footer %}
|
{% block content_footer %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script>
|
|
||||||
// Function to generate a new Chat API Key
|
|
||||||
function generateNewChatApiKey() {
|
|
||||||
generateApiKey('/admin/user/generate_chat_api_key', '#chat-api-key', '#chat-api-key-field');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to generate a new general API Key
|
|
||||||
function generateNewApiKey() {
|
|
||||||
generateApiKey('/admin/user/generate_api_api_key', '#api-key', '#api-key-field');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reusable function to handle API key generation
|
|
||||||
function generateApiKey(url, inputSelector, fieldSelector) {
|
|
||||||
$.ajax({
|
|
||||||
url: url,
|
|
||||||
type: 'POST',
|
|
||||||
contentType: 'application/json',
|
|
||||||
success: function(response) {
|
|
||||||
$(inputSelector).val(response.api_key);
|
|
||||||
$(fieldSelector).show();
|
|
||||||
},
|
|
||||||
error: function(error) {
|
|
||||||
alert('Error generating new API key: ' + error.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to copy text to clipboard
|
|
||||||
function copyToClipboard(selector, messageSelector) {
|
|
||||||
const element = document.querySelector(selector);
|
|
||||||
if (element) {
|
|
||||||
const text = element.value;
|
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
||||||
navigator.clipboard.writeText(text).then(function() {
|
|
||||||
showCopyMessage(messageSelector);
|
|
||||||
}).catch(function(error) {
|
|
||||||
alert('Failed to copy text: ' + error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fallbackCopyToClipboard(text, messageSelector);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('Element not found for selector:', selector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback method for copying text to clipboard
|
|
||||||
function fallbackCopyToClipboard(text, messageSelector) {
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.value = text;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.focus();
|
|
||||||
textArea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
showCopyMessage(messageSelector);
|
|
||||||
} catch (err) {
|
|
||||||
alert('Fallback: Oops, unable to copy', err);
|
|
||||||
}
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to show copy confirmation message
|
|
||||||
function showCopyMessage(messageSelector) {
|
|
||||||
const message = document.querySelector(messageSelector);
|
|
||||||
if (message) {
|
|
||||||
message.style.display = 'block';
|
|
||||||
setTimeout(function() {
|
|
||||||
message.style.display = 'none';
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event listeners for copy buttons
|
|
||||||
document.getElementById('copy-chat-button').addEventListener('click', function() {
|
|
||||||
copyToClipboard('#chat-api-key', '#copy-chat-message');
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('copy-api-button').addEventListener('click', function() {
|
|
||||||
copyToClipboard('#api-key', '#copy-message');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
// JavaScript to detect user's timezone
|
|
||||||
document.addEventListener('DOMContentLoaded', (event) => {
|
|
||||||
// Detect timezone
|
|
||||||
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
||||||
|
|
||||||
// Send timezone to the server via a POST request
|
|
||||||
fetch('/set_user_timezone', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ timezone: userTimezone })
|
|
||||||
}).then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
console.log('Timezone sent to server successfully');
|
|
||||||
} else {
|
|
||||||
console.error('Failed to send timezone to server');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -17,8 +17,15 @@ from config.type_defs.processor_types import PROCESSOR_TYPES
|
|||||||
from .dynamic_form_base import DynamicFormBase
|
from .dynamic_form_base import DynamicFormBase
|
||||||
|
|
||||||
|
|
||||||
|
def validate_catalog_name(form, field):
|
||||||
|
# Controleer of een catalog met deze naam al bestaat
|
||||||
|
existing_catalog = Catalog.query.filter_by(name=field.data).first()
|
||||||
|
if existing_catalog:
|
||||||
|
raise ValidationError(f'A Catalog with name "{field.data}" already exists. Choose another name.')
|
||||||
|
|
||||||
|
|
||||||
class CatalogForm(FlaskForm):
|
class CatalogForm(FlaskForm):
|
||||||
name = StringField('Name', validators=[DataRequired(), Length(max=50)])
|
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_catalog_name])
|
||||||
description = TextAreaField('Description', validators=[Optional()])
|
description = TextAreaField('Description', validators=[Optional()])
|
||||||
|
|
||||||
# Select Field for Catalog Type (Uses the CATALOG_TYPES defined in config)
|
# Select Field for Catalog Type (Uses the CATALOG_TYPES defined in config)
|
||||||
@@ -41,7 +48,7 @@ class CatalogForm(FlaskForm):
|
|||||||
|
|
||||||
|
|
||||||
class EditCatalogForm(DynamicFormBase):
|
class EditCatalogForm(DynamicFormBase):
|
||||||
name = StringField('Name', validators=[DataRequired(), Length(max=50)])
|
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_catalog_name])
|
||||||
description = TextAreaField('Description', validators=[Optional()])
|
description = TextAreaField('Description', validators=[Optional()])
|
||||||
|
|
||||||
# Select Field for Catalog Type (Uses the CATALOG_TYPES defined in config)
|
# Select Field for Catalog Type (Uses the CATALOG_TYPES defined in config)
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""Make Catalog Name Unique
|
||||||
|
|
||||||
|
Revision ID: c71facc0ce7e
|
||||||
|
Revises: d69520ec540d
|
||||||
|
Create Date: 2025-06-07 08:38:23.759681
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import pgvector
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'c71facc0ce7e'
|
||||||
|
down_revision = 'd69520ec540d'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_unique_constraint(None, 'catalog', ['name'])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
pass
|
||||||
|
# ### end Alembic commands ###
|
||||||
Reference in New Issue
Block a user