feat: Implement email sending utilities and templates for job notifications
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m9s
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m9s
- Added email_service.py for sending emails with SMTP configuration. - Introduced email_templates.py to render job alert email subjects and bodies. - Enhanced scraper.py to extract contact information from job listings. - Updated settings.js to handle negative keyword input validation. - Created email.html and email_templates.html for managing email subscriptions and templates in the admin interface. - Modified base.html to include links for email alerts and templates. - Expanded user settings.html to allow management of negative keywords. - Updated utils.py to include functions for retrieving negative keywords and email settings. - Enhanced job filtering logic to exclude jobs containing negative keywords.
This commit is contained in:
102
web/templates/admin/email_templates.html
Normal file
102
web/templates/admin/email_templates.html
Normal file
@@ -0,0 +1,102 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<h2>Email Templates</h2>
|
||||
<section>
|
||||
<h3>Available Templates</h3>
|
||||
{% if not templates %}
|
||||
<p>No templates found. Create one below to get started.</p>
|
||||
{% else %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Slug</th>
|
||||
<th>Status</th>
|
||||
<th>Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for template in templates %}
|
||||
<tr>
|
||||
<td>{{ template.name }}</td>
|
||||
<td>{{ template.slug }}</td>
|
||||
<td>{{ 'Active' if template.is_active else 'Inactive' }}</td>
|
||||
<td>{{ template.updated_at or template.created_at or '' }}</td>
|
||||
<td style="display: flex; gap: 0.5rem;">
|
||||
<a class="button" href="{{ url_for('admin_email_templates', template_id=template.template_id) }}">Edit</a>
|
||||
<a class="button" href="{{ url_for('admin_email_templates', preview_id=template.template_id) }}">Preview</a>
|
||||
<form method="post" onsubmit="return confirm('Delete template {{ template.name }}?');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="action" value="delete" />
|
||||
<input type="hidden" name="template_id" value="{{ template.template_id }}" />
|
||||
<button type="submit">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</section>
|
||||
<section>
|
||||
<h3>{{ 'Edit Template' if editing else 'Create Template' }}</h3>
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="action" value="{{ 'update' if editing else 'create' }}" />
|
||||
{% if editing %}
|
||||
<input type="hidden" name="template_id" value="{{ editing.template_id }}" />
|
||||
{% endif %}
|
||||
<div>
|
||||
<label for="name">Name</label>
|
||||
<input type="text" id="name" name="name" value="{{ editing.name if editing else '' }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="slug">Slug</label>
|
||||
<input type="text" id="slug" name="slug" placeholder="job-alert" value="{{ editing.slug if editing else '' }}" />
|
||||
<small>Leave blank to reuse the name. Slug must be URL friendly (letters, numbers, dashes).</small>
|
||||
</div>
|
||||
<div>
|
||||
<label for="subject">Subject Template</label>
|
||||
<input type="text" id="subject" name="subject" value="{{ editing.subject if editing else '' }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="body">Body Template</label>
|
||||
<textarea id="body" name="body" rows="12" required>{{ editing.body if editing else '' }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" name="is_active" {% if editing is none or editing.is_active %}checked{% endif %} />
|
||||
Active
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit">{{ 'Update Template' if editing else 'Create Template' }}</button>
|
||||
{% if editing %}
|
||||
<a class="button" href="{{ url_for('admin_email_templates') }}">Cancel</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
<aside>
|
||||
<h4>Available placeholders</h4>
|
||||
<ul>
|
||||
<li><code>{count}</code> – number of jobs in the alert</li>
|
||||
<li><code>{count_label}</code> – "No new jobs" or "X new jobs"</li>
|
||||
<li><code>{scope}</code> – formatted region/keyword context</li>
|
||||
<li><code>{region}</code>, <code>{keyword}</code></li>
|
||||
<li><code>{timestamp}</code> – formatted timestamp</li>
|
||||
<li><code>{jobs_section}</code> – newline-prefixed block of job entries</li>
|
||||
<li><code>{jobs_message}</code> – jobs block without leading newline</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</section>
|
||||
{% if preview %}
|
||||
<section>
|
||||
<h3>Preview: {{ preview_template.name if preview_template else 'Job Alert' }}</h3>
|
||||
<article>
|
||||
<h4>Subject</h4>
|
||||
<pre>{{ preview.subject }}</pre>
|
||||
<h4>Body</h4>
|
||||
<pre>{{ preview.body }}</pre>
|
||||
</article>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user